home *** CD-ROM | disk | FTP | other *** search
- (*********************************************************************
- * DSPack 2.3 *
- * DirectShow BaseClass *
- * *
- * home page : http://www.progdigy.com *
- * email : hgourvest@progdigy.com *
- * *
- * date : 21-02-2003 *
- * *
- * The contents of this file are used with permission, subject to *
- * the Mozilla Public License Version 1.1 (the "License"); you may *
- * not use this file except in compliance with the License. You may *
- * obtain a copy of the License at *
- * http://www.mozilla.org/MPL/MPL-1.1.html *
- * *
- * Software distributed under the License is distributed on an *
- * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or *
- * implied. See the License for the specific language governing *
- * rights and limitations under the License. *
- * *
- *********************************************************************)
-
- {.$DEFINE DEBUG} // Debug Log
- {.$DEFINE TRACE} // Trace Criteral Section (DEBUG must be ON)
-
- unit BaseClass;
-
- {$IFDEF VER150}
- {$WARN UNSAFE_CODE OFF}
- {$WARN UNSAFE_TYPE OFF}
- {$WARN UNSAFE_CAST OFF}
- {$ENDIF}
-
- interface
- uses Windows, SysUtils, Classes, Math, ActiveX, Forms, Messages, Controls,
- DirectShow9, DSUtil, dialogs, ComObj;
-
- const
- OATRUE = -1;
- OAFALSE = 0;
-
- type
- TBCCritSec = class
- private
- FCritSec : TRTLCriticalSection;
- {$IFDEF DEBUG}
- FcurrentOwner: Longword;
- FlockCount : Longword;
- FTrace : boolean; // Trace this one
- {$ENDIF}
- public
- constructor Create;
- destructor Destroy; override;
- procedure Lock;
- procedure UnLock;
- function CritCheckIn: boolean;
- function CritCheckOut: boolean;
- {$IFDEF DEBUG}
- property Trace: boolean read FTrace write FTrace;
- {$ENDIF}
- end;
-
- TBCBaseObject = class(TObJect)
- private
- FName: string;
- public
- constructor Create(Name: string);
- class function NewInstance: TObject; override;
- procedure FreeInstance; override;
- class function ObjectsActive: integer;
- end;
-
- TBCClassFactory = Class;
-
- TBCUnknown = class(TBCBaseObject, IUnKnown)
- private
- FRefCount: integer;
- FOwner : Pointer;
- protected
- function IUnknown.QueryInterface = NonDelegatingQueryInterface;
- function IUnknown._AddRef = NonDelegatingAddRef;
- function IUnknown._Release = NonDelegatingRelease;
- function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
- function _AddRef: Integer; stdcall;
- function _Release: Integer; stdcall;
- public
- constructor Create(name: string; Unk: IUnknown);
- constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); virtual;
- function NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
- function NonDelegatingAddRef: Integer; virtual; stdcall;
- function NonDelegatingRelease: Integer; virtual; stdcall;
- function GetOwner: IUnKnown;
- end;
-
- TBCUnknownClass = Class of TBCUnknown;
-
- TFormPropertyPage = class;
- TFormPropertyPageClass = class of TFormPropertyPage;
-
- TBCBaseFilter = class;
- TBCBaseFilterClass = class of TBCBaseFilter;
-
- TBCClassFactory = class(TObject, IUnKnown, IClassFactory)
- private
- FNext : TBCClassFactory;
- FComClass : TBCUnknownClass;
- FPropClass: TFormPropertyPageClass;
- FName : String;
- FClassID : TGUID;
- FCategory : TGUID;
- FMerit : LongWord;
- FPinCount : Cardinal;
- FPins : PRegFilterPins;
- function RegisterFilter(FilterMapper: IFilterMapper; Register: Boolean): boolean; overload;
- function RegisterFilter(FilterMapper: IFilterMapper2; Register: Boolean): boolean; overload;
- procedure UpdateRegistry(Register: Boolean); overload;
- protected
- function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
- function _AddRef: Integer; stdcall;
- function _Release: Integer; stdcall;
- function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
- out Obj): HResult; stdcall;
- function LockServer(fLock: BOOL): HResult; stdcall;
- public
- constructor CreateFilter(ComClass: TBCUnknownClass; Name: string;
- const ClassID: TGUID; const Category: TGUID; Merit: LongWord;
- PinCount: Cardinal; Pins: PRegFilterPins);
- constructor CreatePropertyPage(ComClass: TFormPropertyPageClass; const ClassID: TGUID);
- property Name: String read FName;
- property ClassID: TGUID read FClassID;
- end;
-
-
-
- TBCFilterTemplate = class
- private
- FFactoryList : TBCClassFactory;
- procedure AddObjectFactory(Factory: TBCClassFactory);
- public
- constructor Create;
- destructor Destroy; override;
- function RegisterServer(Register: Boolean): boolean;
- function GetFactoryFromClassID(const CLSID: TGUID): TBCClassFactory;
- end;
-
-
- TBCMediaType = object
- MediaType: PAMMediaType;
- function Equal(mt: TBCMediaType): boolean; overload;
- function Equal(mt: PAMMediaType): boolean; overload;
- function MatchesPartial(Partial: PAMMediaType): boolean;
- function IsPartiallySpecified: boolean;
- function IsValid: boolean;
- procedure InitMediaType;
- function FormatLength: Cardinal;
- end;
-
-
- TBCBasePin = class;
-
- TBCBaseFilter = class(TBCUnknown, IBaseFilter, IAMovieSetup)
- protected
- FState : TFilterState; // current state: running, paused
- FClock : IReferenceClock; // this graph's ref clock
- FStart : TReferenceTime; // offset from stream time to reference time
- FCLSID : TGUID; // This filters clsid used for serialization
- FLock : TBCCritSec; // Object we use for locking
-
- FFilterName : WideString; // Full filter name
- FGRaph : IFilterGraph; // Graph we belong to
- FSink : IMediaEventSink; // Called with notify events
- FPinVersion: Integer; // Current pin version
- public
- constructor Create(Name: string; // Object description
- Unk : IUnKnown; // IUnknown of delegating object
- Lock: TBCCritSec; // Object who maintains lock
- const clsid: TGUID // The clsid to be used to serialize this filter
- ); overload;
-
- constructor Create(Name: string; // Object description
- Unk : IUnKnown; // IUnknown of delegating object
- Lock: TBCCritSec; // Object who maintains lock
- const clsid: TGUID; // The clsid to be used to serialize this filter
- out hr: HRESULT // General OLE return code
- ); overload;
- constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
- destructor destroy; override;
- // --- IPersist method ---
- function GetClassID(out classID: TCLSID): HResult; stdcall;
- // --- IMediaFilter methods ---
- // override Stop and Pause so we can activate the pins.
- // Note that Run will call Pause first if activation needed.
- // Override these if you want to activate your filter rather than
- // your pins.
- function Stop: HRESULT; virtual; stdcall;
- function Pause: HRESULT; virtual; stdcall;
- // the start parameter is the difference to be added to the
- // sample's stream time to get the reference time for
- // its presentation
- function Run(tStart: TReferenceTime): HRESULT; virtual; stdcall;
- function GetState(dwMilliSecsTimeout: DWORD; out State: TFilterState): HRESULT; virtual; stdcall;
- function SetSyncSource(pClock: IReferenceClock): HRESULT; stdcall;
- function GetSyncSource(out pClock: IReferenceClock): HRESULT; stdcall;
- // --- helper methods ---
- // return the current stream time - ie find out what
- // stream time should be appearing now
- function StreamTime(out rtStream: TReferenceTime): HRESULT; virtual;
- // Is the filter currently active?
- function IsActive: boolean;
- // Is this filter stopped (without locking)
- function IsStopped: boolean;
- // --- IBaseFilter methods ---
- // pin enumerator
- function EnumPins(out ppEnum: IEnumPins): HRESULT; stdcall;
- // default behaviour of FindPin assumes pin ids are their names
- function FindPin(Id: PWideChar; out ppPin: IPin): HRESULT; virtual; stdcall;
- function QueryFilterInfo(out pInfo: TFilterInfo): HRESULT; stdcall;
- function JoinFilterGraph(pGraph: IFilterGraph; pName: PWideChar): HRESULT; stdcall;
- // return a Vendor information string. Optional - may return E_NOTIMPL.
- // memory returned should be freed using CoTaskMemFree
- // default implementation returns E_NOTIMPL
- function QueryVendorInfo(out pVendorInfo: PWideChar): HRESULT; stdcall;
- // --- helper methods ---
- // send an event notification to the filter graph if we know about it.
- // returns S_OK if delivered, S_FALSE if the filter graph does not sink
- // events, or an error otherwise.
- function NotifyEvent(EventCode, EventParam1, EventParam2: LongInt): HRESULT;
- // return the filter graph we belong to
- function GetFilterGraph: IFilterGraph;
- // Request reconnect
- // pPin is the pin to reconnect
- // pmt is the type to reconnect with - can be NULL
- // Calls ReconnectEx on the filter graph
- function ReconnectPin(Pin: IPin; pmt: PAMMediaType): HRESULT;
- // find out the current pin version (used by enumerators)
- function GetPinVersion: LongInt; virtual;
- procedure IncrementPinVersion;
- // you need to supply these to access the pins from the enumerator
- // and for default Stop and Pause/Run activation.
- function GetPinCount: integer; virtual; abstract;
- function GetPin(n: Integer): TBCBasePin; virtual; abstract;
- // --- IAMovieSetup methods ---
- function Register: HRESULT; stdcall;
- function Unregister: HRESULT; stdcall;
-
- property State: TFilterState read FState;
- property GRaph : IFilterGraph read FGRaph;
- end;
-
- { NOTE The implementation of this class calls the CUnknown constructor with
- a NULL outer unknown pointer. This has the effect of making us a self
- contained class, ie any QueryInterface, AddRef or Release calls will be
- routed to the class's NonDelegatingUnknown methods. You will typically
- find that the classes that do this then override one or more of these
- virtual functions to provide more specialised behaviour. A good example
- of this is where a class wants to keep the QueryInterface internal but
- still wants its lifetime controlled by the external object }
-
- TBCBasePin = class(TBCUnknown, IPin, IQualityControl)
- private
- FPinName: WideString;
- FConnected : IPin; // Pin we have connected to
- Fdir : TPinDirection; // Direction of this pin
- FLock : TBCCritSec; // Object we use for locking
- FRunTimeError : boolean; // Run time error generated
- FCanReconnectWhenActive: boolean; // OK to reconnect when active
- FTryMyTypesFirst : boolean; // When connecting enumerate
- // this pin's types first
- FFilter : TBCBaseFilter; // Filter we were created by
- FQSink : IQualityControl; // Target for Quality messages
- FTypeVersion : LongInt; // Holds current type version
- Fmt : TAMMediaType; // Media type of connection
-
- FStart : TReferenceTime; // time from NewSegment call
- FStop : TReferenceTime; // time from NewSegment
- FRate : double; // rate from NewSegment
-
- FRef : LongInt;
- function GetCurrentMediaType: TBCMediaType;
- function GetAMMediaType: PAMMediaType;
- protected
- procedure DisplayPinInfo(ReceivePin: IPin);
- procedure DisplayTypeInfo(Pin: IPin; pmt: PAMMediaType);
-
- // used to agree a media type for a pin connection
- // given a specific media type, attempt a connection (includes
- // checking that the type is acceptable to this pin)
- function AttemptConnection(
- ReceivePin: IPin; // connect to this pin
- pmt : PAMMediaType // using this type
- ): HRESULT;
- // try all the media types in this enumerator - for each that
- // we accept, try to connect using ReceiveConnection.
- function TryMediaTypes(
- ReceivePin: IPin; // connect to this pin
- pmt : PAMMediaType; // proposed type from Connect
- Enum : IEnumMediaTypes // try this enumerator
- ): HRESULT;
-
- // establish a connection with a suitable mediatype. Needs to
- // propose a media type if the pmt pointer is null or partially
- // specified - use TryMediaTypes on both our and then the other pin's
- // enumerator until we find one that works.
- function AgreeMediaType(
- ReceivePin: IPin; // connect to this pin
- pmt : PAMMediaType // proposed type from Connect
- ): HRESULT;
- function DisconnectInternal: HRESULT; stdcall;
- public
- function NonDelegatingAddRef: Integer; override; stdcall;
- function NonDelegatingRelease: Integer; override; stdcall;
- constructor Create(
- ObjectName: string; // Object description
- Filter : TBCBaseFilter; // Owning filter who knows about pins
- Lock : TBCCritSec; // Object who implements the lock
- out hr : HRESULT; // General OLE return code
- Name : WideString; // Pin name for us
- dir : TPinDirection); // Either PINDIR_INPUT or PINDIR_OUTPUT
- destructor destroy; override;
- // --- IPin methods ---
- // take lead role in establishing a connection. Media type pointer
- // may be null, or may point to partially-specified mediatype
- // (subtype or format type may be GUID_NULL).
- function Connect(pReceivePin: IPin; const pmt: PAMMediaType): HRESULT; stdcall;
- // (passive) accept a connection from another pin
- function ReceiveConnection(pConnector: IPin; const pmt: TAMMediaType): HRESULT; stdcall;
- function Disconnect: HRESULT; stdcall;
- function ConnectedTo(out pPin: IPin): HRESULT; stdcall;
- function ConnectionMediaType(out pmt: TAMMediaType): HRESULT; stdcall;
- function QueryPinInfo(out pInfo: TPinInfo): HRESULT; stdcall;
- function QueryDirection(out pPinDir: TPinDirection): HRESULT; stdcall;
- function QueryId(out Id: PWideChar): HRESULT; virtual; stdcall;
- // does the pin support this media type
- function QueryAccept(const pmt: TAMMediaType): HRESULT; stdcall;
- // return an enumerator for this pins preferred media types
- function EnumMediaTypes(out ppEnum: IEnumMediaTypes): HRESULT; virtual; stdcall;
- // return an array of IPin* - the pins that this pin internally connects to
- // All pins put in the array must be AddReffed (but no others)
- // Errors: "Can't say" - FAIL, not enough slots - return S_FALSE
- // Default: return E_NOTIMPL
- // The filter graph will interpret NOT_IMPL as any input pin connects to
- // all visible output pins and vice versa.
- // apPin can be NULL if nPin==0 (not otherwise).
- function QueryInternalConnections(out apPin: IPin; var nPin: ULONG): HRESULT; virtual; stdcall;
- // Called when no more data will be sent
- function EndOfStream: HRESULT; virtual; stdcall;
- function BeginFlush: HRESULT; virtual; stdcall; abstract;
- function EndFlush: HRESULT; virtual; stdcall; abstract;
- // Begin/EndFlush still PURE
-
- // NewSegment notifies of the start/stop/rate applying to the data
- // about to be received. Default implementation records data and
- // returns S_OK.
- // Override this to pass downstream.
- function NewSegment(tStart, tStop: TReferenceTime; dRate: double): HRESULT; virtual; stdcall;
- // --- IQualityControl methods ---
- function Notify(pSelf: IBaseFilter; q: TQuality): HRESULT; virtual; stdcall;
- function SetSink(piqc: IQualityControl): HRESULT; virtual; stdcall;
- // --- helper methods ---
-
- // Returns true if the pin is connected. false otherwise.
- function IsConnected: boolean;
- // Return the pin this is connected to (if any)
- property GetConnected: IPin read FConnected;
- // Check if our filter is currently stopped
- function IsStopped: boolean;
- // find out the current type version (used by enumerators)
- function GetMediaTypeVersion: longint; virtual;
- procedure IncrementTypeVersion;
- // switch the pin to active (paused or running) mode
- // not an error to call this if already active
- function Active: HRESULT; virtual;
- // switch the pin to inactive state - may already be inactive
- function Inactive: HRESULT; virtual;
- // Notify of Run() from filter
- function Run(Start: TReferenceTime): HRESULT; virtual;
- // check if the pin can support this specific proposed type and format
- function CheckMediaType(mt: PAMMediaType): HRESULT; virtual; abstract;
- // set the connection to use this format (previously agreed)
- function SetMediaType(mt: PAMMediaType): HRESULT; virtual;
- // check that the connection is ok before verifying it
- // can be overridden eg to check what interfaces will be supported.
- function CheckConnect(Pin: IPin): HRESULT; virtual;
- // Set and release resources required for a connection
- function BreakConnect: HRESULT; virtual;
- function CompleteConnect(ReceivePin: IPin): HRESULT; virtual;
- // returns the preferred formats for a pin
- function GetMediaType(Position: integer; out MediaType: PAMMediaType): HRESULT; virtual;
- // access to NewSegment values
- property CurrentStopTime: TReferenceTime read FStop;
- property CurrentStartTime: TReferenceTime read FStart;
- property CurrentRate: double read FRate;
- // Access name
- property Name: WideString read FPinName;
- property CanReconnectWhenActive: boolean read FCanReconnectWhenActive write FCanReconnectWhenActive;
- // Media type
- property CurrentMediaType: TBCMediaType read GetCurrentMediaType;
- property AMMediaType: PAMMediaType read GetAMMediaType;
- end;
-
- TBCEnumPins = class(TInterfacedObject, IEnumPins)
- private
- FPosition: integer; // Current ordinal position
- FPinCount: integer; // Number of pins available
- FFilter: TBCBaseFilter; // The filter who owns us
- FVersion: LongInt; // Pin version information
- // These pointers have not been AddRef'ed and
- // so they should not be dereferenced. They are
- // merely kept to ID which pins have been enumerated.
- FPinCache: TList;
- { If while we are retrieving a pin for example from the filter an error
- occurs we assume that our internal state is stale with respect to the
- filter (someone may have deleted all the pins). We can check before
- starting whether or not the operation is likely to fail by asking the
- filter what it's current version number is. If the filter has not
- overriden the GetPinVersion method then this will always match }
- function AreWeOutOfSync: boolean;
-
- (* This method performs the same operations as Reset, except is does not clear
- the cache of pins already enumerated. *)
- function Refresh: HRESULT; stdcall;
- public
- constructor Create(Filter: TBCBaseFilter; EnumPins: TBCEnumPins);
- destructor Destroy; override;
-
- function Next(cPins: ULONG; // place this many pins...
- out ppPins: IPin; // ...in this array of IPin*
- pcFetched: PULONG // actual count passed returned here
- ): HRESULT; stdcall;
- function Skip(cPins: ULONG): HRESULT; stdcall;
- function Reset: HRESULT; stdcall;
- function Clone(out ppEnum: IEnumPins): HRESULT; stdcall;
- end;
-
- TBCEnumMediaTypes = class(TInterfacedObject, IEnumMediaTypes)
- private
- FPosition: Cardinal; // Current ordinal position
- FPin : TBCBasePin; // The pin who owns us
- FVersion : LongInt; // Media type version value
- function AreWeOutOfSync: boolean;
- public
- constructor Create(Pin: TBCBasePin; EnumMediaTypes: TBCEnumMediaTypes);
- destructor Destroy; override;
- function Next(cMediaTypes: ULONG; out ppMediaTypes: PAMMediaType;
- pcFetched: PULONG): HRESULT; stdcall;
- function Skip(cMediaTypes: ULONG): HRESULT; stdcall;
- function Reset: HRESULT; stdcall;
- function Clone(out ppEnum: IEnumMediaTypes): HRESULT; stdcall;
- end;
-
-
- TBCBaseOutputPin = class(TBCBasePin)
- private
- FAllocator: IMemAllocator;
- // interface on the downstreaminput pin, set up in CheckConnect when we connect.
- FInputPin : IMemInputPin;
- public
- constructor Create(ObjectName: string; Filter: TBCBaseFilter; Lock: TBCCritSec;
- out hr: HRESULT; Name: WideString);
-
- // override CompleteConnect() so we can negotiate an allocator
- function CompleteConnect(ReceivePin: IPin): HRESULT; override;
- // negotiate the allocator and its buffer size/count and other properties
- // Calls DecideBufferSize to set properties
- function DecideAllocator(Pin: IMemInputPin; out Alloc: IMemAllocator): HRESULT; virtual;
- // override this to set the buffer size and count. Return an error
- // if the size/count is not to your liking.
- // The allocator properties passed in are those requested by the
- // input pin - use eg the alignment and prefix members if you have
- // no preference on these.
- function DecideBufferSize(Alloc: IMemAllocator; propInputRequest: PAllocatorProperties): HRESULT; virtual;
-
- // returns an empty sample buffer from the allocator
- function GetDeliveryBuffer(out Sample: IMediaSample; StartTime: PReferenceTime;
- EndTime: PReferenceTime; Flags: Longword): HRESULT; virtual;
-
- // deliver a filled-in sample to the connected input pin
- // note - you need to release it after calling this. The receiving
- // pin will addref the sample if it needs to hold it beyond the
- // call.
- function Deliver(Sample: IMediaSample): HRESULT; virtual;
-
- // override this to control the connection
- function InitAllocator(out Alloc: IMemAllocator): HRESULT; virtual;
- function CheckConnect(Pin: IPin): HRESULT; override;
- function BreakConnect: HRESULT; override;
-
- // override to call Commit and Decommit
- function Active: HRESULT; override;
- function Inactive: HRESULT; override;
-
- // we have a default handling of EndOfStream which is to return
- // an error, since this should be called on input pins only
- function EndOfStream: HRESULT; override; stdcall;
-
- // called from elsewhere in our filter to pass EOS downstream to
- // our connected input pin
- function DeliverEndOfStream: HRESULT; virtual;
-
- // same for Begin/EndFlush - we handle Begin/EndFlush since it
- // is an error on an output pin, and we have Deliver methods to
- // call the methods on the connected pin
- function BeginFlush: HRESULT; override; stdcall;
- function EndFlush: HRESULT; override; stdcall;
- function DeliverBeginFlush: HRESULT; virtual;
- function DeliverEndFlush: HRESULT; virtual;
-
- // deliver NewSegment to connected pin - you will need to
- // override this if you queue any data in your output pin.
- function DeliverNewSegment(Start, Stop: TReferenceTime; Rate: double): HRESULT; virtual;
- end;
-
- TBCBaseInputPin = class(TBCBasePin, IMemInputPin)
- protected
- FAllocator: IMemAllocator; // Default memory allocator
- // allocator is read-only, so received samples
- // cannot be modified (probably only relevant to in-place
- // transforms
- FReadOnly: boolean;
-
- //private: this should really be private... only the MPEG code
- // currently looks at it directly and it should use IsFlushing().
- // in flushing state (between BeginFlush and EndFlush)
- // if TRUE, all Receives are returned with S_FALSE
- FFlushing: boolean;
-
- // Sample properties - initalized in Receive
-
- FSampleProps: TAMSample2Properties;
-
- public
-
- constructor Create(ObjectName: string; Filter: TBCBaseFilter;
- Lock: TBCCritSec; out hr: HRESULT; Name: WideString);
- destructor Destroy; override;
-
- // ----------IMemInputPin--------------
- // return the allocator interface that this input pin
- // would like the output pin to use
- function GetAllocator(out ppAllocator: IMemAllocator): HRESULT; stdcall;
- // tell the input pin which allocator the output pin is actually
- // going to use.
- function NotifyAllocator(pAllocator: IMemAllocator; bReadOnly: BOOL): HRESULT; stdcall;
- // this method is optional (can return E_NOTIMPL).
- // default implementation returns E_NOTIMPL. Override if you have
- // specific alignment or prefix needs, but could use an upstream
- // allocator
- function GetAllocatorRequirements(out pProps: TAllocatorProperties): HRESULT; stdcall;
- // do something with this media sample
- function Receive(pSample: IMediaSample): HRESULT; virtual; stdcall;
- // do something with these media samples
- function ReceiveMultiple(var pSamples: IMediaSample; nSamples: Longint;
- out nSamplesProcessed: Longint): HRESULT; stdcall;
- // See if Receive() blocks
- function ReceiveCanBlock: HRESULT; stdcall;
-
- //-----------Helper-------------
- // Default handling for BeginFlush - call at the beginning
- // of your implementation (makes sure that all Receive calls
- // fail). After calling this, you need to free any queued data
- // and then call downstream.
- function BeginFlush: HRESULT; override; stdcall;
-
- // default handling for EndFlush - call at end of your implementation
- // - before calling this, ensure that there is no queued data and no thread
- // pushing any more without a further receive, then call downstream,
- // then call this method to clear the m_bFlushing flag and re-enable
- // receives
- function EndFlush: HRESULT; override; stdcall;
-
- // Release the pin's allocator.
- function BreakConnect: HRESULT; override;
-
- // helper method to check the read-only flag
- property IsReadOnly: boolean read FReadOnly;
-
- // helper method to see if we are flushing
- property IsFlushing: boolean read FFlushing;
-
- // Override this for checking whether it's OK to process samples
- // Also call this from EndOfStream.
- function CheckStreaming: HRESULT; virtual;
-
- // Pass a Quality notification on to the appropriate sink
- function PassNotify(const q: TQuality): HRESULT;
-
-
- //================================================================================
- // IQualityControl methods (from CBasePin)
- //================================================================================
-
- function Notify(pSelf: IBaseFilter; q: TQuality): HRESULT; override; stdcall;
-
- // no need to override:
- // STDMETHODIMP SetSink(IQualityControl * piqc);
-
- // switch the pin to inactive state - may already be inactive
- function Inactive: HRESULT; override;
-
- // Return sample properties pointer
- function SampleProps: PAMSample2Properties;
- end;
-
- TBCTransformOutputPin = class;
- TBCTransformInputPin = class;
-
- TBCTransformFilter = class(TBCBaseFilter)
- protected
- FEOSDelivered : boolean; // have we sent EndOfStream
- FSampleSkipped : boolean; // Did we just skip a frame
- FQualityChanged: boolean; // Have we degraded?
- // critical section protecting filter state.
- FcsFilter: TBCCritSec;
- // critical section stopping state changes (ie Stop) while we're
- // processing a sample.
- //
- // This critical section is held when processing
- // events that occur on the receive thread - Receive() and EndOfStream().
- //
- // If you want to hold both m_csReceive and m_csFilter then grab
- // m_csFilter FIRST - like CTransformFilter::Stop() does.
- FcsReceive: TBCCritSec;
- // these hold our input and output pins
- FInput : TBCTransformInputPin;
- FOutput: TBCTransformOutputPin;
- public
- // map getpin/getpincount for base enum of pins to owner
- // override this to return more specialised pin objects
-
- function GetPinCount: integer; override;
- function GetPin(n: integer): TBCBasePin; override;
- function FindPin(Id: PWideChar; out ppPin: IPin): HRESULT; override; stdcall;
-
- // override state changes to allow derived transform filter
- // to control streaming start/stop
- function Stop: HRESULT; override; stdcall;
- function Pause: HRESULT; override; stdcall;
-
- constructor Create(ObjectName: string; unk: IUnKnown; const clsid: TGUID);
- constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
- destructor destroy; override;
-
- // =================================================================
- // ----- override these bits ---------------------------------------
- // =================================================================
-
- // These must be supplied in a derived class
- function Transform(msIn, msout: IMediaSample): HRESULT; virtual;
-
- // check if you can support mtIn
- function CheckInputType(mtIn: PAMMediaType): HRESULT; virtual; abstract;
-
- // check if you can support the transform from this input to this output
- function CheckTransform(mtIn, mtOut: PAMMediaType): HRESULT; virtual; abstract;
-
- // this goes in the factory template table to create new instances
- // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
-
- // call the SetProperties function with appropriate arguments
- function DecideBufferSize(Allocator: IMemAllocator; prop: PAllocatorProperties): HRESULT; virtual; abstract;
-
- // override to suggest OUTPUT pin media types
- function GetMediaType(Position: integer; out MediaType: PAMMediaType): HRESULT; virtual; abstract;
-
-
-
- // =================================================================
- // ----- Optional Override Methods -----------------------
- // =================================================================
-
- // you can also override these if you want to know about streaming
- function StartStreaming: HRESULT; virtual;
- function StopStreaming: HRESULT; virtual;
-
- // override if you can do anything constructive with quality notifications
- function AlterQuality(const q: TQuality): HRESULT; virtual;
-
- // override this to know when the media type is actually set
- function SetMediaType(direction: TPinDirection; pmt: PAMMediaType): HRESULT; virtual;
-
- // chance to grab extra interfaces on connection
- function CheckConnect(dir: TPinDirection; Pin: IPin): HRESULT; virtual;
- function BreakConnect(dir: TPinDirection): HRESULT; virtual;
- function CompleteConnect(direction: TPinDirection; ReceivePin: IPin): HRESULT; virtual;
-
- // chance to customize the transform process
- function Receive(Sample: IMediaSample): HRESULT; virtual;
-
- // Standard setup for output sample
- function InitializeOutputSample(Sample: IMediaSample; out OutSample: IMediaSample): HRESULT;
-
- // if you override Receive, you may need to override these three too
- function EndOfStream: HRESULT; virtual;
- function BeginFlush: HRESULT; virtual;
- function EndFlush: HRESULT; virtual;
- function NewSegment(Start, Stop: TReferenceTime; Rate: double): HRESULT; virtual;
-
- property Input: TBCTransformInputPin read FInput write FInput;
- property Output: TBCTransformOutputPin read FOutPut write FOutput;
-
- end;
-
- TBCTransformInputPin = class(TBCBaseInputPin)
- private
- FTransformFilter: TBCTransformFilter;
- public
- constructor Create(ObjectName: string; TransformFilter: TBCTransformFilter;
- out hr: HRESULT; Name: WideString);
-
- destructor destroy; override;
- function QueryId(out id: PWideChar): HRESULT; override; stdcall;
-
-
- // Grab and release extra interfaces if required
-
- function CheckConnect(Pin: IPin): HRESULT; override;
- function BreakConnect: HRESULT; override;
- function CompleteConnect(ReceivePin: IPin): HRESULT; override;
-
- // check that we can support this output type
- function CheckMediaType(mtIn: PAMMediaType): HRESULT; override;
-
- // set the connection media type
- function SetMediaType(mt: PAMMediaType): HRESULT; override;
-
- // --- IMemInputPin -----
-
- // here's the next block of data from the stream.
- // AddRef it yourself if you need to hold it beyond the end
- // of this call.
- function Receive(pSample: IMediaSample): HRESULT; override; stdcall;
-
- // provide EndOfStream that passes straight downstream
- // (there is no queued data)
- function EndOfStream: HRESULT; override; stdcall;
-
- // passes it to CTransformFilter::BeginFlush
- function BeginFlush: HRESULT; override; stdcall;
-
- // passes it to CTransformFilter::EndFlush
- function EndFlush: HRESULT; override; stdcall;
-
- function NewSegment(Start, Stop: TReferenceTime; Rate: double): HRESULT; override; stdcall;
-
- // Check if it's OK to process samples
- function CheckStreaming: HRESULT; override;
- end;
-
- TBCTransformOutputPin = class(TBCBaseOutputPin)
- private
- FTransformFilter: TBCTransformFilter;
- // implement IMediaPosition by passing upstream
- FPosition: IUnknown;
- public
- constructor Create(ObjectName: string; TransformFilter: TBCTransformFilter;
- out hr: HRESULT; Name: WideString);
- destructor destroy; override;
- // override to expose IMediaPosition
- function NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult; override;
-
- // --- TBCBaseOutputPin ------------
-
- function QueryId(out Id: PWideChar): HRESULT; override; stdcall;
- // Grab and release extra interfaces if required
- function CheckConnect(Pin: IPin): HRESULT; override;
- function BreakConnect: HRESULT; override;
- function CompleteConnect(ReceivePin: IPin): HRESULT; override;
-
- // check that we can support this output type
- function CheckMediaType(mtOut: PAMMediaType): HRESULT; override;
-
- // set the connection media type
- function SetMediaType(pmt: PAMMediaType): HRESULT; override;
-
- // called from CBaseOutputPin during connection to ask for
- // the count and size of buffers we need.
- function DecideBufferSize(Alloc: IMemAllocator; Prop: PAllocatorProperties): HRESULT; override;
-
- // returns the preferred formats for a pin
- function GetMediaType(Position: integer; out MediaType: PAMMediaType): HRESULT; override;
-
- // inherited from IQualityControl via CBasePin
- function Notify(Sendr: IBaseFilter; q: TQuality): HRESULT; override; stdcall;
- end;
-
- TBCTransInPlaceOutputPin = class;
- TBCTransInPlaceInputPin = class;
-
- TBCTransInPlaceFilter = class(TBCTransformFilter)
- public
- // map getpin/getpincount for base enum of pins to owner
- // override this to return more specialised pin objects
- function GetPin(n: integer): TBCBasePin; override;
-
- // Set bModifiesData == false if your derived filter does
- // not modify the data samples (for instance it's just copying
- // them somewhere else or looking at the timestamps).
- constructor Create(ObjectName: string; unk: IUnKnown; clsid: TGUID;
- out hr: HRESULT; ModifiesData: boolean = true);
-
- constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
-
- // The following are defined to avoid undefined pure virtuals.
- // Even if they are never called, they will give linkage warnings/errors
-
- // We override EnumMediaTypes to bypass the transform class enumerator
- // which would otherwise call this.
- function GetMediaType(Position: integer; out MediaType: PAMMediaType): HRESULT; override;
-
- // This is called when we actually have to provide out own allocator.
- function DecideBufferSize(Alloc: IMemAllocator; propInputRequest: PAllocatorProperties): HRESULT; override;
-
- // The functions which call this in CTransform are overridden in this
- // class to call CheckInputType with the assumption that the type
- // does not change. In Debug builds some calls will be made and
- // we just ensure that they do not assert.
- function CheckTransform(mtIn, mtOut: PAMMediaType): HRESULT; override;
-
- // =================================================================
- // ----- You may want to override this -----------------------------
- // =================================================================
-
- function CompleteConnect(dir: TPinDirection; ReceivePin: IPin): HRESULT; override;
-
- // chance to customize the transform process
- function Receive(Sample: IMediaSample): HRESULT; override;
-
- // =================================================================
- // ----- You MUST override these -----------------------------------
- // =================================================================
-
- function Transform(Sample: IMediaSample): HRESULT; reintroduce; virtual; abstract;
-
- // this goes in the factory template table to create new instances
- // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
-
- protected
- FModifiesData: boolean; // Does this filter change the data?
- function Copy(Source: IMediaSample): IMediaSample;
-
- // these hold our input and output pins
- function InputPin: TBCTransInPlaceInputPin;
- function OutputPin: TBCTransInPlaceOutputPin;
-
- // Helper to see if the input and output types match
- function TypesMatch: boolean;
-
- // Are the input and output allocators different?
- function UsingDifferentAllocators: boolean;
- end;
-
- TBCTransInPlaceInputPin = class(TBCTransformInputPin)
- protected
- FTIPFilter: TBCTransInPlaceFilter; // our filter
- FReadOnly : boolean; // incoming stream is read only
- public
- constructor Create(ObjectName: string; Filter: TBCTransInPlaceFilter;
- out hr: HRESULT; Name: WideString);
- // --- IMemInputPin -----
- // Provide an enumerator for media types by getting one from downstream
- function EnumMediaTypes(out ppEnum: IEnumMediaTypes): HRESULT; override; stdcall;
-
- // Say whether media type is acceptable.
- function CheckMediaType(pmt: PAMMediaType): HRESULT; override;
-
- // Return our upstream allocator
- function GetAllocator(out Allocator: IMemAllocator): HRESULT; stdcall;
-
- // get told which allocator the upstream output pin is actually
- // going to use.
- function NotifyAllocator(Allocator: IMemAllocator; ReadOnly: BOOL): HRESULT; stdcall;
-
- // Allow the filter to see what allocator we have
- // N.B. This does NOT AddRef
- function PeekAllocator: IMemAllocator;
-
- // Pass this on downstream if it ever gets called.
- function GetAllocatorRequirements(props: PAllocatorProperties): HRESULT; stdcall;
-
- property ReadOnly: Boolean read FReadOnly;
- end;
-
-
- // ==================================================
- // Implements the output pin
- // ==================================================
-
- TBCTransInPlaceOutputPin = class(TBCTransformOutputPin)
- protected
- // m_pFilter points to our CBaseFilter
- FTIPFilter: TBCTransInPlaceFilter;
- public
- constructor Create(ObjectName: string; Filter: TBCTransInPlaceFilter;
- out hr: HRESULT; Name: WideString);
-
- // --- CBaseOutputPin ------------
-
- // negotiate the allocator and its buffer size/count
- // Insists on using our own allocator. (Actually the one upstream of us).
- // We don't override this - instead we just agree the default
- // then let the upstream filter decide for itself on reconnect
- // virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc);
-
- // Provide a media type enumerator. Get it from upstream.
- function EnumMediaTypes(out ppEnum: IEnumMediaTypes): HRESULT; override; stdcall;
-
- // Say whether media type is acceptable.
- function CheckMediaType(pmt: PAMMediaType): HRESULT; override;
-
- // This just saves the allocator being used on the output pin
- // Also called by input pin's GetAllocator()
- procedure SetAllocator(Allocator: IMemAllocator);
-
- function ConnectedIMemInputPin: IMemInputPin;
-
- // Allow the filter to see what allocator we have
- // N.B. This does NOT AddRef
- function PeekAllocator: IMemAllocator;
- end;
-
-
- TBCBasePropertyPage = class(TBCUnknown, IPropertyPage)
- private
- FObjectSet: boolean; // SetObject has been called or not.
- protected
- FPageSite: IPropertyPageSite; // Details for our property site
- FDirty: boolean; // Has anything been changed
- FForm: TFormPropertyPage;
- public
- constructor Create(Name: String; Unk: IUnKnown; Form: TFormPropertyPage);
- destructor Destroy; override;
- procedure SetPageDirty;
-
- { IPropertyPage }
- function SetPageSite(const pageSite: IPropertyPageSite): HResult; stdcall;
- function Activate(hwndParent: HWnd; const rc: TRect; bModal: BOOL): HResult; stdcall;
- function Deactivate: HResult; stdcall;
- function GetPageInfo(out pageInfo: TPropPageInfo): HResult; stdcall;
- function SetObjects(cObjects: Longint; pUnkList: PUnknownList): HResult; stdcall;
- function Show(nCmdShow: Integer): HResult; stdcall;
- function Move(const rect: TRect): HResult; stdcall;
- function IsPageDirty: HResult; stdcall;
- function Apply: HResult; stdcall;
- function Help(pszHelpDir: POleStr): HResult; stdcall;
- function TranslateAccelerator(msg: PMsg): HResult; stdcall;
- end;
-
- TOnConnect = procedure(sender: Tobject; Unknown: IUnknown) of object;
-
- TFormPropertyPage = class(TForm, IUnKnown, IPropertyPage)
- private
- FPropertyPage: TBCBasePropertyPage;
- published
- function OnConnect(Unknown: IUnknown): HRESULT; virtual;
- function OnDisconnect: HRESULT; virtual;
- function OnApplyChanges: HRESULT; virtual;
- property PropertyPage : TBCBasePropertyPage read FPropertyPage implements IUnKnown, IPropertyPage;
- end;
-
- TBCBaseDispatch = class{IDispatch}
- protected
- FTI: ITypeInfo;
- public
- // IDispatch methods
- function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
- function GetTypeInfo(const iid: TGUID; info: Cardinal; lcid: LCID; out tinfo): HRESULT; stdcall;
- function GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
- end;
-
- TBCMediaControl = class(TBCUnknown, IDispatch)
- public
- FBaseDisp: TBCBaseDispatch;
- constructor Create(name: string; unk: IUnknown);
- destructor Destroy; override;
-
- // IDispatch methods
- function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
- function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
- function GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
- function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
- Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
- end;
-
- TBCMediaEvent = class(TBCUnknown, IDisPatch{,IMediaEventEx})
- protected
- FBasedisp: TBCBaseDispatch;
- public
- constructor Create(Name: string; Unk: IUnknown);
- destructor destroy; override;
- // IDispatch methods
- function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
- function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
- function GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
- function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
- Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
- end;
-
- TBCMediaPosition = class(TBCUnknown, IDispatch {IMediaPosition})
- protected
- FBaseDisp: TBCBaseDispatch;
- public
- constructor Create(Name: String; Unk: IUnknown); overload;
- constructor Create(Name: String; Unk: IUnknown; out hr: HRESULT); overload;
- destructor Destroy; override;
- // IDispatch methods
- function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
- function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
- function GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
- function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
- Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
- end;
-
-
- // A utility class that handles IMediaPosition and IMediaSeeking on behalf
- // of single-input pin renderers, or transform filters.
- //
- // Renderers will expose this from the filter; transform filters will
- // expose it from the output pin and not the renderer.
- //
- // Create one of these, giving it your IPin* for your input pin, and delegate
- // all IMediaPosition methods to it. It will query the input pin for
- // IMediaPosition and respond appropriately.
- //
- // Call ForceRefresh if the pin connection changes.
- //
- // This class no longer caches the upstream IMediaPosition or IMediaSeeking
- // it acquires it on each method call. This means ForceRefresh is not needed.
- // The method is kept for source compatibility and to minimise the changes
- // if we need to put it back later for performance reasons.
-
- TBCPosPassThru = class(TBCMediaPosition, IMediaSeeking)
- protected
- FPin: IPin;
- function GetPeer(out MP: IMediaPosition): HRESULT;
- function GetPeerSeeking(out MS: IMediaSeeking): HRESULT;
- public
-
- constructor Create(name: String; Unk: IUnknown; out hr: HRESULT; Pin: IPin);
- function ForceRefresh: HRESULT;{return S_OK;}
-
- // override to return an accurate current position
- function GetMediaTime(out StartTime, EndTime: int64): HRESULT; virtual;
-
- // IMediaSeeking methods
- function GetCapabilities(out pCapabilities: DWORD): HRESULT; stdcall;
- function CheckCapabilities(var pCapabilities: DWORD): HRESULT; stdcall;
- function IsFormatSupported(const pFormat: TGUID): HRESULT; stdcall;
- function QueryPreferredFormat(out pFormat: TGUID): HRESULT; stdcall;
- function GetTimeFormat(out pFormat: TGUID): HRESULT; stdcall;
- function IsUsingTimeFormat(const pFormat: TGUID): HRESULT; stdcall;
- function SetTimeFormat(const pFormat: TGUID): HRESULT; stdcall;
- function GetDuration(out pDuration: int64): HRESULT; stdcall;
- function GetStopPosition(out pStop: int64): HRESULT; stdcall;
- function GetCurrentPosition(out pCurrent: int64): HRESULT; stdcall;
- function ConvertTimeFormat(out pTarget: int64; pTargetFormat: PGUID;
- Source: int64; pSourceFormat: PGUID): HRESULT; stdcall;
- function SetPositions(var pCurrent: int64; dwCurrentFlags: DWORD;
- var pStop: int64; dwStopFlags: DWORD): HRESULT; stdcall;
- function GetPositions(out pCurrent, pStop: int64): HRESULT; stdcall;
- function GetAvailable(out pEarliest, pLatest: int64): HRESULT; stdcall;
- function SetRate(dRate: double): HRESULT; stdcall;
- function GetRate(out pdRate: double): HRESULT; stdcall;
- function GetPreroll(out pllPreroll: int64): HRESULT; stdcall;
-
- // IMediaPosition properties
- function get_Duration(out plength: TRefTime): HResult; stdcall;
- function put_CurrentPosition(llTime: TRefTime): HResult; stdcall;
- function get_CurrentPosition(out pllTime: TRefTime): HResult; stdcall;
- function get_StopTime(out pllTime: TRefTime): HResult; stdcall;
- function put_StopTime(llTime: TRefTime): HResult; stdcall;
- function get_PrerollTime(out pllTime: TRefTime): HResult; stdcall;
- function put_PrerollTime(llTime: TRefTime): HResult; stdcall;
- function put_Rate(dRate: double): HResult; stdcall;
- function get_Rate(out pdRate: double): HResult; stdcall;
- function CanSeekForward(out pCanSeekForward: Longint): HResult; stdcall;
- function CanSeekBackward(out pCanSeekBackward: Longint): HResult; stdcall;
- end;
-
- TBCRendererPosPassThru = class(TBCPosPassThru)
- protected
- FPositionLock: TBCCritSec; // Locks access to our position
- FStartMedia : Int64; // Start media time last seen
- FEndMedia : Int64; // And likewise the end media
- FReset : boolean; // Have media times been set
- public
- // Used to help with passing media times through graph
- constructor Create(name: String; Unk: IUnknown; out hr: HRESULT; Pin: IPin); reintroduce;
- destructor destroy; override;
-
- function RegisterMediaTime(MediaSample: IMediaSample): HRESULT; overload;
- function RegisterMediaTime(StartTime, EndTime: int64): HRESULT; overload;
- function GetMediaTime(out StartTime, EndTime: int64): HRESULT; override;
- function ResetMediaTime: HRESULT;
- function EOS: HRESULT;
- end;
-
- // wrapper for event objects
- TBCAMEvent = class
- protected
- FEvent: THANDLE;
- public
- constructor Create(ManualReset: boolean = false);
- destructor destroy; override;
- property Handle: THandle read FEvent;
- procedure SetEv;
- function Wait(Timeout: Cardinal = INFINITE): boolean;
- procedure Reset;
- function Check: boolean;
- end;
-
-
- TBCRenderedInputPin = class(TBCBaseInputPin)
- private
- procedure DoCompleteHandling;
- protected
- // Member variables to track state
- FAtEndOfStream : boolean; // Set by EndOfStream
- FCompleteNotified : boolean; // Set when we notify for EC_COMPLETE
- public
- constructor Create(ObjectName: string; Filter: TBCBaseFilter;
- Lock: TBCCritSec; out hr: HRESULT; Name: WideString);
-
- // Override methods to track end of stream state
- function EndOfStream: HRESULT; override; stdcall;
- function EndFlush: HRESULT; override; stdcall;
-
- function Active: HRESULT; override;
- function Run(Start: TReferenceTime): HRESULT; override;
- end;
-
-
- function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult; stdcall;
- function DllCanUnloadNow: HResult; stdcall;
- function DllRegisterServer: HResult; stdcall;
- function DllUnregisterServer: HResult; stdcall;
-
- procedure DbgLog(obj: TBCBaseObJect; msg: string);
- {
- function MTEqual(MT1, MT2: PAMMediaType): boolean;
- function MTMatchesPartial(Source, Partial: PAMMediaType): boolean;
- function MTIsPartiallySpecified(mt: PAMMediaType): boolean;
- function MTIsValid(mt: PAMMediaType): boolean;
- procedure MTInitMediaType(mt: PAMMediaType); }
-
- implementation
- //uses ComObj;
-
- var
- ObjectCount : Integer;
- FactoryCount : Integer;
- TemplatesVar : TBCFilterTemplate;
-
- {$IFDEF DEBUG}
- DebugLog: TStringList;
- {$ENDIF}
-
- procedure DbgLog(obj: TBCBaseObJect; msg: string);
- begin
- {$IFDEF DEBUG}
- if obj = nil then DebugLog.Add(TimeToStr(time) +' > '+ msg) else
- DebugLog.Add(TimeToStr(time) +' > '+ format('Object: %s, msg: %s.',[obj.FName, msg]));
- OutputDebugString(PChar(DebugLog.Strings[DebugLog.Count-1]));
- {$ENDIF}
-
- end;
-
- // -----------------------------------------------------------------------------
- // TBCMediaType
- // -----------------------------------------------------------------------------
-
- function TBCMediaType.Equal(mt: TBCMediaType): boolean;
- begin
- result := ((IsEqualGUID(Mediatype.majortype,mt.MediaType.majortype) = TRUE) and
- (IsEqualGUID(Mediatype.subtype,mt.MediaType.subtype) = TRUE) and
- (IsEqualGUID(Mediatype.formattype,mt.MediaType.formattype) = TRUE) and
- (Mediatype.cbFormat = mt.MediaType.cbFormat) and
- ( (Mediatype.cbFormat = 0) or
- (CompareMem(Mediatype.pbFormat, mt.MediaType.pbFormat, Mediatype.cbFormat))));
- end;
-
- function TBCMediaType.Equal(mt: PAMMediaType): boolean;
- begin
- result := ((IsEqualGUID(Mediatype.majortype,mt.majortype) = TRUE) and
- (IsEqualGUID(Mediatype.subtype,mt.subtype) = TRUE) and
- (IsEqualGUID(Mediatype.formattype,mt.formattype) = TRUE) and
- (Mediatype.cbFormat = mt.cbFormat) and
- ( (Mediatype.cbFormat = 0) or
- (CompareMem(Mediatype.pbFormat, mt.pbFormat, Mediatype.cbFormat))));
- end;
-
- function TBCMediaType.MatchesPartial(Partial: PAMMediaType): boolean;
- begin
- result := false;
- if (not IsEqualGUID(partial.majortype, GUID_NULL) and
- not IsEqualGUID(MediaType.majortype, partial.majortype)) then exit;
-
- if (not IsEqualGUID(partial.subtype, GUID_NULL) and
- not IsEqualGUID(MediaType.subtype, partial.subtype)) then exit;
-
- if not IsEqualGUID(partial.formattype, GUID_NULL) then
- begin
- if not IsEqualGUID(MediaType.formattype, partial.formattype) then exit;
- if (MediaType.cbFormat <> partial.cbFormat) then exit;
- if ((MediaType.cbFormat <> 0) and
- (CompareMem(MediaType.pbFormat, partial.pbFormat, MediaType.cbFormat) <> false)) then exit;
- end;
- result := true;
- end;
-
- function TBCMediaType.IsPartiallySpecified: boolean;
- begin
- if (IsEqualGUID(Mediatype.majortype, GUID_NULL) or
- IsEqualGUID(Mediatype.formattype, GUID_NULL)) then result := true
- else result := false;
- end;
-
- function TBCMediaType.IsValid: boolean;
- begin
- result := not IsEqualGUID(MediaType.majortype,GUID_NULL);
- end;
-
- procedure TBCMediaType.InitMediaType;
- begin
- ZeroMemory(MediaType, sizeof(TAMMediaType));
- MediaType.lSampleSize := 1;
- MediaType.bFixedSizeSamples := TRUE;
- end;
-
- function TBCMediaType.FormatLength: Cardinal;
- begin
- result := MediaType.cbFormat
- end;
- {
- function MTMatchesPartial(Source, Partial: PAMMediaType): boolean;
- begin
- result := false;
- if (not IsEqualGUID(partial.majortype, GUID_NULL) and
- not IsEqualGUID(Source.majortype, partial.majortype)) then exit;
-
- if (not IsEqualGUID(partial.subtype, GUID_NULL) and
- not IsEqualGUID(Source.subtype, partial.subtype)) then exit;
-
- if not IsEqualGUID(partial.formattype, GUID_NULL) then
- begin
- if not IsEqualGUID(Source.formattype, partial.formattype) then exit;
- if (Source.cbFormat <> partial.cbFormat) then exit;
- if ((Source.cbFormat <> 0) and
- (CompareMem(Source.pbFormat, partial.pbFormat, Source.cbFormat) <> false)) then exit;
- end;
- result := true;
- end;
-
- function MTIsPartiallySpecified(mt: PAMMediaType): boolean;
- begin
- if (IsEqualGUID(mt.majortype, GUID_NULL) or
- IsEqualGUID(mt.formattype, GUID_NULL)) then result := true
- else result := false;
- end;
-
- function MTEqual(MT1, MT2: PAMMediaType): boolean;
- begin
- // I don't believe we need to check sample size or
- // temporal compression flags, since I think these must
- // be represented in the type, subtype and format somehow. They
- // are pulled out as separate flags so that people who don't understand
- // the particular format representation can still see them, but
- // they should duplicate information in the format block.
- result := ((IsEqualGUID(MT1.majortype,MT2.majortype) = TRUE) and
- (IsEqualGUID(MT1.subtype,MT2.subtype) = TRUE) and
- (IsEqualGUID(MT1.formattype,MT2.formattype) = TRUE) and
- (MT1.cbFormat = MT2.cbFormat) and
- ( (MT1.cbFormat = 0) or
- (CompareMem(MT1.pbFormat, MT2.pbFormat, MT1.cbFormat))));
- end;
-
- function MTIsValid(mt: PAMMediaType): boolean;
- begin
- result := not IsEqualGUID(mt.majortype,GUID_NULL);
- end;
-
- procedure MTInitMediaType(mt: PAMMediaType);
- begin
- ZeroMemory(mt, sizeof(TAMMediaType));
- mt.lSampleSize := 1;
- mt.bFixedSizeSamples := TRUE;
- end; }
-
- // -----------------------------------------------------------------------------
-
- function AMGetWideString(Source: WideString; out Dest: PWideChar): HRESULT;
- type TWideCharArray = array of WideChar;
- var NameLen: Cardinal;
- begin
- if Source = '' then
- begin
- dest := nil;
- result := S_OK;
- exit;
- end;
- assert(@dest <> nil);
- nameLen := (length(Source)+1)*2;
- Dest := CoTaskMemAlloc(nameLen);
- if(Dest = nil) then
- begin
- result := E_OUTOFMEMORY;
- exit;
- end;
- CopyMemory(dest, pointer(Source), nameLen-1);
- TWideCharArray(dest)[(nameLen div 2)-1] := #0;
- result := NOERROR;
- end;
-
- // -----------------------------------------------------------------------------
-
-
- function CreateMemoryAllocator(out Allocator: IMemAllocator): HRESULT;
- begin
- result := CoCreateInstance(CLSID_MemoryAllocator, nil, CLSCTX_INPROC_SERVER,
- IID_IMemAllocator, Allocator);
- end;
-
- // Put this one here rather than in ctlutil.cpp to avoid linking
- // anything brought in by ctlutil.cpp
- function CreatePosPassThru(Agg: IUnknown; Renderer: boolean; Pin: IPin; out PassThru: IUnknown): HRESULT; stdcall;
- var
- UnkSeek: IUnknown;
- APassThru: ISeekingPassThru;
- begin
- PassThru := nil;
-
- result := CoCreateInstance(CLSID_SeekingPassThru, Agg, CLSCTX_INPROC_SERVER,
- IUnknown, UnkSeek);
- if FAILED(result) then exit;
-
- result := UnkSeek.QueryInterface(IID_ISeekingPassThru, APassThru);
- if FAILED(result) then
- begin
- UnkSeek := nil;
- exit;
- end;
-
- result := APassThru.Init(Renderer, Pin);
- APassThru := nil;
- if FAILED(result) then
- begin
- UnkSeek := nil;
- exit;
- end;
-
- PassThru := UnkSeek;
- result := S_OK;
- end;
-
- // -----------------------------------------------------------------------------
-
- function Templates: TBCFilterTemplate;
- begin
- if TemplatesVar = nil then TemplatesVar := TBCFilterTemplate.Create;
- result := TemplatesVar;
- end;
-
- function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult; stdcall;
- var
- Factory: TBCClassFactory;
- begin
- Factory := Templates.GetFactoryFromClassID(CLSID);
- if Factory <> nil then
- if Factory.GetInterface(IID, Obj) then
- Result := S_OK
- else
- Result := E_NOINTERFACE
- else
- begin
- Pointer(Obj) := nil;
- Result := CLASS_E_CLASSNOTAVAILABLE;
- end;
- end;
-
- function DllCanUnloadNow: HResult; stdcall;
- begin
- if (ObjectCount = 0) and (FactoryCount = 0) then
- result := S_OK else result := S_FALSE;;
- end;
-
- function DllRegisterServer: HResult; stdcall;
- begin
- if Templates.RegisterServer(true) then result := S_OK else result := E_FAIL;
- end;
-
- function DllUnregisterServer: HResult; stdcall;
- begin
- if Templates.RegisterServer(false) then result := S_OK else result := E_FAIL;
- end;
-
- { TBCClassFactory }
-
- constructor TBCClassFactory.CreateFilter(ComClass: TBCUnknownClass; Name: string;
- const ClassID: TGUID; const Category: TGUID; Merit: LongWord;
- PinCount: Cardinal; Pins: PRegFilterPins);
- begin
- Templates.AddObjectFactory(Self);
- FComClass := ComClass;
- FName := Name;
- FClassID := ClassID;
- FCategory := Category;
- FMerit := Merit;
- FPinCount := PinCount;
- FPins := Pins;
- end;
-
- constructor TBCClassFactory.CreatePropertyPage(ComClass: TFormPropertyPageClass; const ClassID: TGUID);
- begin
- Templates.AddObjectFactory(Self);
- FPropClass := ComClass;
- FClassID := ClassID;
- end;
-
- function TBCClassFactory.CreateInstance(const unkOuter: IUnKnown;
- const iid: TIID; out obj): HResult;
- var
- ComObject: TBCUnknown;
- PropObject: TFormPropertyPage;
- begin
- if @obj = nil then
- begin
- Result := E_POINTER;
- Exit;
- end;
- Pointer(obj) := nil;
- if FPropClass <> nil then
- begin
- PropObject := TFormPropertyPageClass(FPropClass).Create(nil);
- PropObject.FPropertyPage := TBCBasePropertyPage.Create('',nil, PropObject);
- Result := PropObject.QueryInterface(IID, obj);
- end
- else
- begin
- ComObject := TBCUnknownClass(FComClass).CreateFromFactory(self, unkOuter);
- Result := ComObject.QueryInterface(IID, obj);
- if ComObject.FRefCount = 0 then ComObject.Free;
- end;
- end;
-
- procedure TBCClassFactory.UpdateRegistry(Register: Boolean);
- var
- FileName: array[0..MAX_PATH-1] of Char;
- ClassID, ServerKeyName: String;
- begin
- ClassID := GUIDToString(FClassID);
- ServerKeyName := 'CLSID\' + ClassID + '\' + 'InprocServer32';
- if Register then
- begin
- CreateRegKey('CLSID\' + ClassID, '', FName);
- GetModuleFileName(hinstance, FileName, MAX_PATH);
- CreateRegKey(ServerKeyName, '', FileName);
- CreateRegKey(ServerKeyName, 'ThreadingModel', 'Both');
- end else
- begin
- DeleteRegKey(ServerKeyName);
- DeleteRegKey('CLSID\' + ClassID);
- end;
- end;
-
- function TBCClassFactory.RegisterFilter(FilterMapper: IFilterMapper; Register: Boolean): boolean;
- type
- TDynArrayPins = array of TRegFilterPins;
- TDynArrayPinType = array of TRegPinTypes;
- var
- i, j: integer;
- FilterGUID: TGUID;
- begin
- result := Succeeded(FilterMapper.UnregisterFilter(FClassID));
- if Register then
- begin
- result := Succeeded(FilterMapper.RegisterFilter(FClassID, StringToOleStr(FName), FMerit));
- if result then
- begin
- for i := 0 to FPinCount - 1 do
- begin
- if TDynArrayPins(FPins)[i].oFilter = nil then
- FilterGUID := GUID_NULL else
- FilterGUID := TDynArrayPins(FPins)[i].oFilter^;
- result := Succeeded(FilterMapper.RegisterPin(FClassID,
- TDynArrayPins(FPins)[i].strName,
- TDynArrayPins(FPins)[i].bRendered,
- TDynArrayPins(FPins)[i].bOutput,
- TDynArrayPins(FPins)[i].bZero,
- TDynArrayPins(FPins)[i].bMany,
- FilterGUID,
- TDynArrayPins(FPins)[i].strConnectsToPin));
- if result then
- begin
- for j := 0 to TDynArrayPins(FPins)[i].nMediaTypes - 1 do
- begin
- result := Succeeded(FilterMapper.RegisterPinType(FClassID,
- TDynArrayPins(FPins)[i].strName,
- TDynArrayPinType(TDynArrayPins(FPins)[i].lpMediaType)[j].clsMajorType^,
- TDynArrayPinType(TDynArrayPins(FPins)[i].lpMediaType)[j].clsMinorType^));
- if not result then break;
- end;
- if not result then break;
- end;
- if not result then break;
- end;
- end;
- end;
- end;
-
- function TBCClassFactory.RegisterFilter(FilterMapper: IFilterMapper2; Register: Boolean): boolean;
- var
- RegFilter: TRegFilter2;
- begin
- result := Succeeded(FilterMapper.UnregisterFilter(FCategory, nil, FClassID));
- if Register then
- begin
- RegFilter.dwVersion := 1;
- RegFilter.dwMerit := FMerit;
- RegFilter.cPins := FPinCount;
- RegFilter.rgPins := FPins;
- result := Succeeded(FilterMapper.RegisterFilter(FClassID, PWideChar(WideString(FName)),
- nil, @FCategory, nil, RegFilter));
- end;
- end;
-
- function TBCClassFactory._AddRef: Integer;
- begin
- result := InterlockedIncrement(FactoryCount);
- end;
-
- function TBCClassFactory._Release: Integer;
- begin
- result := InterlockedDecrement(FactoryCount);
- end;
-
- function TBCClassFactory.LockServer(fLock: BOOL): HResult;
- begin
- Result := CoLockObjectExternal(Self, fLock, True);
- if flock then InterlockedIncrement(ObjectCount)
- else InterlockedDecrement(ObjectCount);
- end;
-
- function TBCClassFactory.QueryInterface(const IID: TGUID; out Obj): HResult;
- begin
- if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE;
- end;
-
- { TBCFilterTemplate }
-
- procedure TBCFilterTemplate.AddObjectFactory(Factory: TBCClassFactory);
- begin
- Factory.FNext := FFactoryList;
- FFactoryList := Factory;
- end;
-
- constructor TBCFilterTemplate.Create;
- begin
- FFactoryList := nil;
- end;
-
- destructor TBCFilterTemplate.Destroy;
- var AFactory: TBCClassFactory;
- begin
- while FFactoryList <> nil do
- begin
- AFactory := FFactoryList;
- FFactoryList := AFactory.FNext;
- AFactory.Free;
- end;
- inherited Destroy;
- end;
-
- function TBCFilterTemplate.GetFactoryFromClassID(const CLSID: TGUID): TBCClassFactory;
- var AFactory: TBCClassFactory;
- begin
- result := nil;
- AFactory := FFactoryList;
- while AFactory <> nil do
- begin
- if IsEqualGUID(CLSID, AFactory.FClassID) then
- begin
- result := AFactory;
- break;
- end;
- AFactory := AFactory.FNext;
- end;
- end;
-
- function TBCFilterTemplate.RegisterServer(Register: Boolean): boolean;
- var
- Filename: array[0..MAX_PATH-1] of Char;
- FilterMapper : IFilterMapper;
- FilterMapper2: IFilterMapper2;
- Factory: TBCClassFactory;
- begin
- result := false;
- GetModuleFileName(hinstance, Filename, sizeof(Filename));
- if Failed(CoCreateInstance(CLSID_FilterMapper2, nil, CLSCTX_INPROC_SERVER, IFilterMapper2, FilterMapper2)) then
- if Failed(CoCreateInstance(CLSID_FilterMapper, nil, CLSCTX_INPROC_SERVER, IFilterMapper, FilterMapper)) then exit;
-
- Factory := FFactoryList;
- while Factory <> nil do
- begin
- Factory.UpdateRegistry(false);
- if FilterMapper2 <> nil then
- result := Factory.RegisterFilter(FilterMapper2, Register)
- else result := Factory.RegisterFilter(FilterMapper, Register);
- if not result then break else Factory.UpdateRegistry(register);
- Factory := Factory.FNext;
- end;
- FilterMapper := nil;
- FilterMapper2 := nil;
- end;
-
- { TBCBaseObject }
-
- constructor TBCBaseObject.Create(Name: string);
- begin
- FName := name;
- end;
-
- procedure TBCBaseObject.FreeInstance;
- begin
- inherited;
- InterlockedDecrement(ObjectCount);
- end;
-
- class function TBCBaseObject.NewInstance: TObject;
- begin
- result := inherited NewInstance;
- InterlockedIncrement(ObjectCount);
- end;
-
- class function TBCBaseObject.ObjectsActive: integer;
- begin
- result := ObjectCount;
- end;
-
- { TBCUnknown }
-
- function TBCUnknown.QueryInterface(const IID: TGUID; out Obj): HResult;
- begin
- if FOwner <> nil then
- Result := IUnknown(FOwner).QueryInterface(IID, Obj)
- else
- Result := NonDelegatingQueryInterface(IID, Obj);
- end;
-
- function TBCUnknown._AddRef: Integer;
- begin
- if FOwner <> nil then
- Result := IUnknown(FOwner)._AddRef else
- Result := NonDelegatingAddRef;
- end;
-
- function TBCUnknown._Release: Integer;
- begin
- if FOwner <> nil then
- Result := IUnknown(FOwner)._Release else
- Result := NonDelegatingRelease;
- end;
-
- function TBCUnknown.NonDelegatingQueryInterface(const IID: TGUID;
- out Obj): HResult;
- begin
- if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE;
- end;
-
- function TBCUnknown.NonDelegatingAddRef: Integer;
- begin
- Result := InterlockedIncrement(FRefCount);
- end;
-
- function TBCUnknown.NonDelegatingRelease: Integer;
- begin
- Result := InterlockedDecrement(FRefCount);
- if Result = 0 then Destroy;
- end;
-
- function TBCUnknown.GetOwner: IUnKnown;
- begin
- result := IUnKnown(FOwner);
- end;
-
- constructor TBCUnknown.Create(name: string; Unk: IUnKnown);
- begin
- inherited Create(name);
- FOwner := Pointer(Unk);
- end;
-
- constructor TBCUnknown.CreateFromFactory(Factory: TBCClassFactory;
- const Controller: IUnKnown);
- begin
- Create(Factory.FName, Controller);
- end;
-
- { TBCBaseFilter }
-
- constructor TBCBaseFilter.Create(Name: string; Unk: IUnKnown;
- Lock: TBCCritSec; const clsid: TGUID);
- begin
- inherited Create(Name, Unk);
- FLock := Lock;
- Fclsid := clsid;
- FState := State_Stopped;
- FClock := nil;
- FGraph := nil;
- FSink := nil;
- FFilterName := '';
- FPinVersion := 1;
- Assert(FLock <> nil, 'Lock = nil !');
- end;
-
- constructor TBCBaseFilter.Create(Name: string; Unk: IUnKnown;
- Lock: TBCCritSec; const clsid: TGUID; out hr: HRESULT);
- begin
- Create(Name, Unk, Lock, clsid);
- assert(@hr <> nil, 'Unreferenced parameter: hr');
- end;
-
- constructor TBCBaseFilter.CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown);
- begin
- Create(Factory.FName,Controller, TBCCritSec.Create, Factory.FClassID);
- end;
-
- destructor TBCBaseFilter.destroy;
- begin
- FFilterName := '';
- FClock := nil;
- FLock.Free;
- inherited;
- end;
-
- function TBCBaseFilter.EnumPins(out ppEnum: IEnumPins): HRESULT;
- begin
- // Create a new ref counted enumerator
- ppEnum := TBCEnumPins.Create(self, nil);
- if ppEnum = nil then result := E_OUTOFMEMORY else result := NOERROR;
- end;
-
- function TBCBaseFilter.FindPin(Id: PWideChar; out ppPin: IPin): HRESULT;
- var
- i: integer;
- pin: TBCBasePin;
- begin
- // We're going to search the pin list so maintain integrity
- FLock.Lock;
- try
- for i := 0 to GetPinCount - 1 do
- begin
- Pin := GetPin(i);
- ASSERT(Pin <> nil);
- if (Pin.FPinName = WideString(Id)) then
- begin
- // Found one that matches
- // AddRef() and return it
- ppPin := Pin;
- result := S_OK;
- exit;
- end;
- end;
- ppPin := nil;
- result := VFW_E_NOT_FOUND;
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCBaseFilter.GetClassID(out classID: TCLSID): HResult;
- begin
- classID := FCLSID;
- result := NOERROR;
- end;
-
- function TBCBaseFilter.GetFilterGraph: IFilterGraph;
- begin
- result := FGRaph;
- end;
-
- function TBCBaseFilter.GetPinVersion: LongInt;
- begin
- result := FPinVersion;
- end;
-
- function TBCBaseFilter.GetState(dwMilliSecsTimeout: DWORD;
- out State: TFilterState): HRESULT;
- begin
- State := FState;
- result := S_OK;
- end;
-
- function TBCBaseFilter.GetSyncSource(out pClock: IReferenceClock): HRESULT;
- begin
- FLock.Lock;
- try
- pClock := FClock;
- finally
- result := NOERROR;
- FLock.UnLock;
- end;
- end;
-
- procedure TBCBaseFilter.IncrementPinVersion;
- begin
- InterlockedIncrement(FPinVersion)
- end;
-
- function TBCBaseFilter.IsActive: boolean;
- begin
- FLock.Lock;
- try
- result := ((FState = State_Paused) or (FState = State_Running));
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCBaseFilter.IsStopped: boolean;
- begin
- result := (FState = State_Stopped);
- end;
-
- function TBCBaseFilter.JoinFilterGraph(pGraph: IFilterGraph;
- pName: PWideChar): HRESULT;
- begin
- FLock.Lock;
- try
- //Henri: This implementation seem to be stupid but it's the exact conversion ?????
- // NOTE: we no longer hold references on the graph (m_pGraph, m_pSink)
- Pointer(FGraph) := Pointer(pGraph);
- if (FGraph <> nil) then
- begin
- if FAILED(FGraph.QueryInterface(IID_IMediaEventSink, FSink)) then
- ASSERT(FSink = nil)
- else FSink := nil; // we do NOT keep a reference on it.
- end
- else
- begin
- // if graph pointer is null, then we should
- // also release the IMediaEventSink on the same object - we don't
- // refcount it, so just set it to null
- Pointer(FSink) := nil;
- end;
-
- FFilterName := '';
- if assigned(pName) then FFilterName := WideString(pName);
- result := NOERROR;
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCBaseFilter.NotifyEvent(EventCode, EventParam1,
- EventParam2: Integer): HRESULT;
- begin
- // Snapshot so we don't have to lock up
- if assigned(FSink) then
- begin
- if (EC_COMPLETE = EventCode) then EventParam2 := LongInt(self);
- result := FSink.Notify(EventCode, EventParam1, EventParam2);
- end
- else
- result := E_NOTIMPL;
- end;
-
- function TBCBaseFilter.Pause: HRESULT;
- var
- c: integer;
- pin: TBCBasePin;
- begin
- FLock.Lock;
- try
- if FState = State_Stopped then
- begin
- for c := 0 to GetPinCount - 1 do
- begin
- Pin := GetPin(c);
- // Disconnected pins are not activated - this saves pins
- // worrying about this state themselves
- if Pin.IsConnected then
- begin
- result := Pin.Active;
- if FAILED(result) then exit;
- end;
- end;
- end;
- // notify all pins of the change to active state
- FState := State_Paused;
- result := S_OK;
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCBaseFilter.QueryFilterInfo(out pInfo: TFilterInfo): HRESULT;
- begin
- if (FFilterName <> '') then
- move(Pointer(FFilterName)^, pInfo.achName, length(FFilterName) * 2 + 2)
- else
- pInfo.achName[0] := #0;
- pInfo.pGraph := FGraph;
- result := NOERROR;
- end;
-
- function TBCBaseFilter.QueryVendorInfo(out pVendorInfo: PWideChar): HRESULT;
- begin
- result := E_NOTIMPL;
- end;
-
- function TBCBaseFilter.ReconnectPin(Pin: IPin; pmt: PAMMediaType): HRESULT;
- var Graph2: IFilterGraph2;
- begin
- if (FGraph <> nil) then
- begin
- result := FGraph.QueryInterface(IID_IFilterGraph2, Graph2);
- if Succeeded(result) then
- begin
- result := Graph2.ReconnectEx(Pin, pmt);
- Graph2 := nil;
- end
- else
- result := FGraph.Reconnect(Pin);
- end
- else
- result := E_NOINTERFACE;
- end;
-
- function TBCBaseFilter.Register: HRESULT;
- var
- Filename: array[0..MAX_PATH-1] of Char;
- FilterMapper : IFilterMapper;
- FilterMapper2: IFilterMapper2;
- Factory: TBCClassFactory;
- AResult : boolean;
- begin
- Aresult := false;
- Result := S_FALSE;
- Factory := Templates.GetFactoryFromClassID(FCLSID);
- if Factory <> nil then
- begin
- GetModuleFileName(hinstance, Filename, sizeof(Filename));
- if Failed(CoCreateInstance(CLSID_FilterMapper2, nil, CLSCTX_INPROC_SERVER, IFilterMapper2, FilterMapper2)) then
- if Failed(CoCreateInstance(CLSID_FilterMapper, nil, CLSCTX_INPROC_SERVER, IFilterMapper, FilterMapper)) then exit;
- Factory.UpdateRegistry(false);
- if FilterMapper2 <> nil then
- AResult := Factory.RegisterFilter(FilterMapper2, true)
- else AResult := Factory.RegisterFilter(FilterMapper, true);
- if Aresult then Factory.UpdateRegistry(true);
- FilterMapper := nil;
- FilterMapper2 := nil;
- end;
- if AResult then result := S_OK else result := S_False;
- end;
-
- function TBCBaseFilter.Run(tStart: TReferenceTime): HRESULT;
- var
- c: integer;
- Pin: TBCBasePin;
- begin
- FLock.Lock;
- try
- // remember the stream time offset
- FStart := tStart;
- if FState = State_Stopped then
- begin
- result := Pause;
- if FAILED(result) then exit;
- end;
- // notify all pins of the change to active state
- if (FState <> State_Running) then
- begin
- for c := 0 to GetPinCount - 1 do
- begin
- Pin := GetPin(c);
- // Disconnected pins are not activated - this saves pins
- // worrying about this state themselves
- if Pin.IsConnected then
- begin
- result := Pin.Run(tStart);
- if FAILED(result) then exit;
- end;
- end;
- end;
- FState := State_Running;
- result := S_OK;
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCBaseFilter.SetSyncSource(pClock: IReferenceClock): HRESULT;
- begin
- FLock.Lock;
- try
- FClock := pClock;
- finally
- result := NOERROR;
- FLock.UnLock;
- end;
- end;
-
- function TBCBaseFilter.Stop: HRESULT;
- var
- c: integer;
- Pin: TBCBasePin;
- hr: HResult;
- begin
- FLock.Lock;
- try
- result := NOERROR;
- // notify all pins of the state change
- if (FState <> State_Stopped) then
- begin
- for c := 0 to GetPinCount - 1 do
- begin
- Pin := GetPin(c);
- // Disconnected pins are not activated - this saves pins worrying
- // about this state themselves. We ignore the return code to make
- // sure everyone is inactivated regardless. The base input pin
- // class can return an error if it has no allocator but Stop can
- // be used to resync the graph state after something has gone bad
- if Pin.IsConnected then
- begin
- hr := Pin.Inactive;
- if (Failed(hr) and SUCCEEDED(result)) then result := hr;
- end;
- end;
- end;
- FState := State_Stopped;
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCBaseFilter.StreamTime(out rtStream: TReferenceTime): HRESULT;
- begin
- // Caller must lock for synchronization
- // We can't grab the filter lock because we want to be able to call
- // this from worker threads without deadlocking
- if FClock = nil then
- begin
- result := VFW_E_NO_CLOCK;
- exit;
- end;
- // get the current reference time
- result := FClock.GetTime(PInt64(@rtStream)^);
- if FAILED(result) then exit;
- // subtract the stream offset to get stream time
- rtStream := rtStream - FStart;
- result := S_OK;
- end;
-
- function TBCBaseFilter.Unregister: HRESULT;
- var
- Filename: array[0..MAX_PATH-1] of Char;
- FilterMapper : IFilterMapper;
- FilterMapper2: IFilterMapper2;
- Factory: TBCClassFactory;
- AResult : boolean;
- begin
- Aresult := false;
- Result := S_FALSE;
- Factory := Templates.GetFactoryFromClassID(FCLSID);
- if Factory <> nil then
- begin
- GetModuleFileName(hinstance, Filename, sizeof(Filename));
- if Failed(CoCreateInstance(CLSID_FilterMapper2, nil, CLSCTX_INPROC_SERVER, IFilterMapper2, FilterMapper2)) then
- if Failed(CoCreateInstance(CLSID_FilterMapper, nil, CLSCTX_INPROC_SERVER, IFilterMapper, FilterMapper)) then exit;
- Factory.UpdateRegistry(false);
- if FilterMapper2 <> nil then
- AResult := Factory.RegisterFilter(FilterMapper2, false)
- else AResult := Factory.RegisterFilter(FilterMapper, false);
- if Aresult then Factory.UpdateRegistry(false);
- FilterMapper := nil;
- FilterMapper2 := nil;
- end;
- if AResult then result := S_OK else result := S_False;
- end;
-
- { TBCEnumPins }
-
- constructor TBCEnumPins.Create(Filter: TBCBaseFilter; EnumPins: TBCEnumPins);
- var i: integer;
- begin
- FPosition := 0;
- FPinCount := 0;
- FFilter := Filter;
- FPinCache := TList.Create;
-
- // We must be owned by a filter derived from CBaseFilter
- ASSERT(FFilter <> nil);
-
- // Hold a reference count on our filter
- FFilter._AddRef;
-
- // Are we creating a new enumerator
- if (EnumPins = nil) then
- begin
- FVersion := FFilter.GetPinVersion;
- FPinCount := FFilter.GetPinCount;
- end
- else
- begin
- ASSERT(FPosition <= FPinCount);
- FPosition := EnumPins.FPosition;
- FPinCount := EnumPins.FPinCount;
- FVersion := EnumPins.FVersion;
- FPinCache.Clear;
- if EnumPins.FPinCache.Count > 0 then
- for i := 0 to EnumPins.FPinCache.Count - 1 do
- FPinCache.Add(EnumPins.FPinCache.Items[i]);
- end;
- end;
-
- destructor TBCEnumPins.Destroy;
- begin
- FPinCache.Free;
- FFilter._Release;
- inherited Destroy;
- end;
-
- function TBCEnumPins.Clone(out ppEnum: IEnumPins): HRESULT;
- begin
- result := NOERROR;
- // Check we are still in sync with the filter
- if AreWeOutOfSync then
- begin
- ppEnum := nil;
- result := VFW_E_ENUM_OUT_OF_SYNC;
- end
- else
- begin
- ppEnum := TBCEnumPins.Create(FFilter, self);
- if ppEnum = nil then result := E_OUTOFMEMORY;
- end;
- end;
-
- function TBCEnumPins.Next(cPins: ULONG; out ppPins: IPin;
- pcFetched: PULONG): HRESULT;
- type
- TPointerDynArray = array of Pointer;
- TIPinDynArray = array of IPin;
- var
- Fetched: cardinal;
- RealPins: integer;
- Pin: TBCBasePin;
- begin
- if pcFetched <> nil then
- pcFetched^ := 0
- else
- if (cPins>1) then
- begin
- result := E_INVALIDARG;
- exit;
- end;
- Fetched := 0; // increment as we get each one.
-
- // Check we are still in sync with the filter
- // If we are out of sync, we should refresh the enumerator.
- // This will reset the position and update the other members, but
- // will not clear cache of pins we have already returned.
- if AreWeOutOfSync then
- Refresh;
-
- // Calculate the number of available pins
- RealPins := min(FPinCount - FPosition, cPins);
- if RealPins = 0 then
- begin
- result := S_FALSE;
- exit;
- end;
-
- { Return each pin interface NOTE GetPin returns CBasePin * not addrefed
- so we must QI for the IPin (which increments its reference count)
- If while we are retrieving a pin from the filter an error occurs we
- assume that our internal state is stale with respect to the filter
- (for example someone has deleted a pin) so we
- return VFW_E_ENUM_OUT_OF_SYNC }
-
- while RealPins > 0 do
- begin
- // Get the next pin object from the filter */
- inc(FPosition);
- Pin := FFilter.GetPin(FPosition-1);
- if Pin = nil then
- begin
- // If this happend, and it's not the first time through, then we've got a problem,
- // since we should really go back and release the iPins, which we have previously
- // AddRef'ed.
- ASSERT(Fetched = 0);
- result := VFW_E_ENUM_OUT_OF_SYNC;
- exit;
- end;
-
- // We only want to return this pin, if it is not in our cache
- if FPinCache.IndexOf(Pin) = -1 then
- begin
- // From the object get an IPin interface
- TPointerDynArray(@ppPins)[Fetched] := nil;
- TIPinDynArray(@ppPins)[Fetched] := Pin;
- inc(Fetched);
- FPinCache.Add(Pin);
- dec(RealPins);
- end;
- end;
-
- if (pcFetched <> nil) then pcFetched^ := Fetched;
-
- if (cPins = Fetched) then result := NOERROR else result := S_FALSE;
- end;
-
- function TBCEnumPins.Skip(cPins: ULONG): HRESULT;
- var PinsLeft: Cardinal;
- begin
- // Check we are still in sync with the filter
- if AreWeOutOfSync then
- begin
- result := VFW_E_ENUM_OUT_OF_SYNC;
- exit;
- end;
-
- // Work out how many pins are left to skip over
- // We could position at the end if we are asked to skip too many...
- // ..which would match the base implementation for CEnumMediaTypes::Skip
-
- PinsLeft := FPinCount - FPosition;
- if (cPins > PinsLeft) then
- begin
- result := S_FALSE;
- exit;
- end;
-
- inc(FPosition, cPins);
- result := NOERROR;
- end;
-
- function TBCEnumPins.Reset: HRESULT;
- begin
- FVersion := FFilter.GetPinVersion;
- FPinCount := FFilter.GetPinCount;
- FPosition := 0;
- FPinCache.Clear;
- result := S_OK;
- end;
-
- function TBCEnumPins.Refresh: HRESULT;
- begin
- FVersion := FFilter.GetPinVersion;
- FPinCount := FFilter.GetPinCount;
- Fposition := 0;
- result := S_OK;
- end;
-
- function TBCEnumPins.AreWeOutOfSync: boolean;
- begin
- if FFilter.GetPinVersion = FVersion then result:= FALSE else result := TRUE;
- end;
-
- { TBCBasePin }
-
- { Called by IMediaFilter implementation when the state changes from Stopped
- to either paused or running and in derived classes could do things like
- commit memory and grab hardware resource (the default is to do nothing) }
-
- function TBCBasePin.Active: HRESULT;
- begin
- result := NOERROR;
- end;
-
- { This is called to make the connection, including the task of finding
- a media type for the pin connection. pmt is the proposed media type
- from the Connect call: if this is fully specified, we will try that.
- Otherwise we enumerate and try all the input pin's types first and
- if that fails we then enumerate and try all our preferred media types.
- For each media type we check it against pmt (if non-null and partially
- specified) as well as checking that both pins will accept it. }
-
- function TBCBasePin.AgreeMediaType(ReceivePin: IPin; pmt: PAMMediaType): HRESULT;
- var
- EnumMT: IEnumMediaTypes;
- hrFailure: HResult;
- i: integer;
- begin
- ASSERT(ReceivePin <> nil);
-
- // if the media type is fully specified then use that
- if ((pmt <> nil) and (not TBCMediaType(pmt).IsPartiallySpecified)) then
- begin
- // if this media type fails, then we must fail the connection
- // since if pmt is nonnull we are only allowed to connect
- // using a type that matches it.
- result := AttemptConnection(ReceivePin, pmt);
- exit;
- end;
-
-
- // Try the other pin's enumerator
- hrFailure := VFW_E_NO_ACCEPTABLE_TYPES;
- for i := 0 to 1 do
- begin
- if (i = byte(FTryMyTypesFirst)) then
- result := ReceivePin.EnumMediaTypes(EnumMT)
- else result := EnumMediaTypes(EnumMT);
-
- if Succeeded(Result) then
- begin
- Assert(EnumMT <> nil);
- result := TryMediaTypes(ReceivePin,pmt,EnumMT);
- EnumMT := nil;
- if Succeeded(result) then
- begin
- result := NOERROR;
- exit;
- end
- else
- begin
- // try to remember specific error codes if there are any
- if ((result <> E_FAIL) and
- (result <> E_INVALIDARG) and
- (result <> VFW_E_TYPE_NOT_ACCEPTED)) then hrFailure := result;
- end;
- end;
- end;
- result := hrFailure;
- end;
-
- function TBCBasePin.AttemptConnection(ReceivePin: IPin; pmt: PAMMediaType): HRESULT;
- begin
-
- // The caller should hold the filter lock becasue this function
- // uses m_Connected. The caller should also hold the filter lock
- // because this function calls SetMediaType(), IsStopped() and
- // CompleteConnect().
- ASSERT(FLock.CritCheckIn);
-
- // Check that the connection is valid -- need to do this for every
- // connect attempt since BreakConnect will undo it.
- result := CheckConnect(ReceivePin);
- if FAILED(result) then
- begin
- DbgLog(self, 'CheckConnect failed');
- // Since the procedure is already returning an error code, there
- // is nothing else this function can do to report the error.
- Assert(SUCCEEDED(BreakConnect));
- exit;
- end;
-
- DisplayTypeInfo(ReceivePin, pmt);
-
- // Check we will accept this media type
-
- result := CheckMediaType(pmt);
- if (result = NOERROR) then
- begin
- // Make ourselves look connected otherwise ReceiveConnection
- // may not be able to complete the connection
- FConnected := ReceivePin;
- result := SetMediaType(pmt);
- if Succeeded(result) then
- begin
- // See if the other pin will accept this type */
- result := ReceivePin.ReceiveConnection(self, pmt^);
- if Succeeded(result) then
- begin
- // Complete the connection
- result := CompleteConnect(ReceivePin);
- if Succeeded(result) then exit
- else
- begin
- DbgLog(self, 'Failed to complete connection');
- ReceivePin.Disconnect;
- end;
- end;
- end;
- end
- else
- begin
- // we cannot use this media type
- // return a specific media type error if there is one
- // or map a general failure code to something more helpful
- // (in particular S_FALSE gets changed to an error code)
- if (SUCCEEDED(result) or (result = E_FAIL) or (result = E_INVALIDARG)) then
- result := VFW_E_TYPE_NOT_ACCEPTED;
- end;
-
- // BreakConnect and release any connection here in case CheckMediaType
- // failed, or if we set anything up during a call back during
- // ReceiveConnection.
-
- // Since the procedure is already returning an error code, there
- // is nothing else this function can do to report the error.
- Assert(Succeeded(BreakConnect));
-
- // If failed then undo our state
- FConnected := nil;
- end;
-
- { This is called when we realise we can't make a connection to the pin and
- must undo anything we did in CheckConnect - override to release QIs done }
-
- function TBCBasePin.BreakConnect: HRESULT;
- begin
- result := NOERROR;
- end;
-
- { This is called during Connect() to provide a virtual method that can do
- any specific check needed for connection such as QueryInterface. This
- base class method just checks that the pin directions don't match }
-
- function TBCBasePin.CheckConnect(Pin: IPin): HRESULT;
- var pd: TPinDirection;
- begin
- // Check that pin directions DONT match
- Pin.QueryDirection(pd);
- ASSERT((pd = PINDIR_OUTPUT) or (pd = PINDIR_INPUT));
- ASSERT((Fdir = PINDIR_OUTPUT) or (Fdir = PINDIR_INPUT));
-
- // we should allow for non-input and non-output connections?
- if (pd = Fdir) then result := VFW_E_INVALID_DIRECTION
- else result := NOERROR;
- end;
-
- { Called when we want to complete a connection to another filter. Failing
- this will also fail the connection and disconnect the other pin as well }
-
- function TBCBasePin.CompleteConnect(ReceivePin: IPin): HRESULT;
- begin
- result := NOERROR;
- end;
-
- { Asked to connect to a pin. A pin is always attached to an owning filter
- object so we always delegate our locking to that object. We first of all
- retrieve a media type enumerator for the input pin and see if we accept
- any of the formats that it would ideally like, failing that we retrieve
- our enumerator and see if it will accept any of our preferred types }
-
- function TBCBasePin.Connect(pReceivePin: IPin; const pmt: PAMMediaType): HRESULT;
- var HR: HResult;
- begin
- FLock.Lock;
- try
- DisplayPinInfo(pReceivePin);
- // See if we are already connected
- if FConnected <> nil then
- begin
- DbgLog(self, 'Already connected');
- result := VFW_E_ALREADY_CONNECTED;
- end;
-
- // See if the filter is active
- if (not IsStopped) and (not FCanReconnectWhenActive) then
- begin
- result := VFW_E_NOT_STOPPED;
- exit;
- end;
-
- // Find a mutually agreeable media type -
- // Pass in the template media type. If this is partially specified,
- // each of the enumerated media types will need to be checked against
- // it. If it is non-null and fully specified, we will just try to connect
- // with this.
- Hr := AgreeMediaType(pReceivePin, pmt);
- if Failed(hr) then
- begin
- DbgLog(self, 'Failed to agree type');
- // Since the procedure is already returning an error code, there
- // is nothing else this function can do to report the error.
- ASSERT(SUCCEEDED(BreakConnect));
- result := HR;
- exit;
- end;
- DbgLog(self, 'Connection succeeded');
- result := NOERROR;
- finally
- FLock.UnLock;
- end;
- end;
-
- // Return an AddRef()'d pointer to the connected pin if there is one
-
- function TBCBasePin.ConnectedTo(out pPin: IPin): HRESULT;
- begin
- // It's pointless to lock here.
- // The caller should ensure integrity.
- pPin := FConnected;
- if (pPin <> nil) then
- result := S_OK
- else result := VFW_E_NOT_CONNECTED;
- end;
-
- function TBCBasePin.ConnectionMediaType(out pmt: TAMMediaType): HRESULT;
- begin
- FLock.Lock;
- try
- // Copy constructor of m_mt allocates the memory
- if IsConnected then
- begin
- CopyMediaType(@pmt,@Fmt);
- result := S_OK;
- end
- else
- begin
- zeromemory(@pmt, SizeOf(TAMMediaType));
- pmt.lSampleSize := 1;
- pmt.bFixedSizeSamples := true;
- result := VFW_E_NOT_CONNECTED;
- end;
- finally
-
- FLock.UnLock;
- end;
- end;
-
- constructor TBCBasePin.Create(ObjectName: string; Filter: TBCBaseFilter;
- Lock: TBCCritSec; out hr: HRESULT; Name: WideString;
- dir: TPinDirection);
- begin
- inherited Create(ObjectName, nil);
- FFilter := Filter;
- FLock := Lock;
- FPinName := Name;
- FConnected := nil;
- Fdir := dir;
- FRunTimeError := FALSE;
- FQSink := nil;
- FTypeVersion := 1;
- FStart := 0;
- FStop := MAX_TIME;
- FCanReconnectWhenActive := false;
- FTryMyTypesFirst := false;
- FRate := 1.0;
- { WARNING - Filter is often not a properly constituted object at
- this state (in particular QueryInterface may not work) - this
- is because its owner is often its containing object and we
- have been called from the containing object's constructor so
- the filter's owner has not yet had its CUnknown constructor
- called.}
-
- FRef := 0; // debug
- ZeroMemory(@fmt, SizeOf(TAMMediaType));
- ASSERT(Filter <> nil);
- ASSERT(Lock <> nil);
- end;
-
- destructor TBCBasePin.destroy;
- begin
- // We don't call disconnect because if the filter is going away
- // all the pins must have a reference count of zero so they must
- // have been disconnected anyway - (but check the assumption)
- ASSERT(FConnected = nil);
- FPinName := '';
- Assert(FRef = 0);
- FreeMediaType(@fmt);
- inherited Destroy;
- end;
-
- // Called when we want to terminate a pin connection
-
- function TBCBasePin.Disconnect: HRESULT;
- begin
- FLock.Lock;
- try
- // See if the filter is active
- if not IsStopped then
- result := VFW_E_NOT_STOPPED
- else result := DisconnectInternal;
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCBasePin.DisconnectInternal: HRESULT;
- begin
- ASSERT(FLock.CritCheckIn);
- if (FConnected <> nil) then
- begin
- result := BreakConnect;
- if FAILED(result) then
- begin
- // There is usually a bug in the program if BreakConnect() fails.
- DbgLog(self, 'WARNING: BreakConnect() failed in CBasePin::Disconnect().');
- exit;
- end;
- FConnected := nil;
- result := S_OK;
- exit;
- end
- else
- // no connection - not an error
- result := S_FALSE;
- end;
-
- procedure TBCBasePin.DisplayPinInfo(ReceivePin: IPin);
- {$IFDEF DEBUG}
- const
- BadPin : WideString = 'Bad Pin';
- var
- ConnectPinInfo, ReceivePinInfo: TPinInfo;
- begin
- if FAILED(QueryPinInfo(ConnectPinInfo)) then
- move(Pointer(BadPin)^, ConnectPinInfo.achName, length(BadPin) * 2 +2)
- else ConnectPinInfo.pFilter := nil;
- if FAILED(ReceivePin.QueryPinInfo(ReceivePinInfo)) then
- move(Pointer(BadPin)^, ReceivePinInfo.achName, length(BadPin) * 2 +2)
- else ReceivePinInfo.pFilter := nil;
- DbgLog(self, 'Trying to connect Pins :');
- DbgLog(self, format(' <%s>', [ConnectPinInfo.achName]));
- DbgLog(self, format(' <%s>', [ReceivePinInfo.achName]));
- {$ELSE}
- begin
- {$ENDIF}
- end;
-
- procedure TBCBasePin.DisplayTypeInfo(Pin: IPin; pmt: PAMMediaType);
- begin
- {$IFDEF DEBUG}
- DbgLog(self, 'Trying media type:');
- DbgLog(self, ' major type: '+ GuidToString(pmt.majortype));
- DbgLog(self, ' sub type : '+ GuidToString(pmt.subtype));
- DbgLog(self, GetMediaTypeDescription(pmt^));
- {$ENDIF}
-
- end;
-
- // Called when no more data will arrive
-
- function TBCBasePin.EndOfStream: HRESULT;
- begin
- result := S_OK;
- end;
-
- { This can be called to return an enumerator for the pin's list of preferred
- media types. An input pin is not obliged to have any preferred formats
- although it can do. For example, the window renderer has a preferred type
- which describes a video image that matches the current window size. All
- output pins should expose at least one preferred format otherwise it is
- possible that neither pin has any types and so no connection is possible }
-
- function TBCBasePin.EnumMediaTypes(out ppEnum: IEnumMediaTypes): HRESULT;
- begin
- // Create a new ref counted enumerator
- ppEnum := TBCEnumMediaTypes.Create(self, nil);
- if (ppEnum = nil) then result := E_OUTOFMEMORY
- else result := NOERROR;
- end;
-
-
- { This is a virtual function that returns a media type corresponding with
- place iPosition in the list. This base class simply returns an error as
- we support no media types by default but derived classes should override }
-
- function TBCBasePin.GetMediaType(Position: integer;
- out MediaType: PAMMediaType): HRESULT;
- begin
- result := E_UNEXPECTED;;
- end;
-
-
- { This is a virtual function that returns the current media type version.
- The base class initialises the media type enumerators with the value 1
- By default we always returns that same value. A Derived class may change
- the list of media types available and after doing so it should increment
- the version either in a method derived from this, or more simply by just
- incrementing the m_TypeVersion base pin variable. The type enumerators
- call this when they want to see if their enumerations are out of date }
-
- function TBCBasePin.GetMediaTypeVersion: longint;
- begin
- result := FTypeVersion;
- end;
-
- { Also called by the IMediaFilter implementation when the state changes to
- Stopped at which point you should decommit allocators and free hardware
- resources you grabbed in the Active call (default is also to do nothing) }
-
- function TBCBasePin.Inactive: HRESULT;
- begin
- FRunTimeError := FALSE;
- result := NOERROR;
- end;
-
- // Increment the cookie representing the current media type version
-
- procedure TBCBasePin.IncrementTypeVersion;
- begin
- InterlockedIncrement(FTypeVersion);
- end;
-
- function TBCBasePin.IsConnected: boolean;
- begin
- result := FConnected <> nil;
- end;
-
- function TBCBasePin.IsStopped: boolean;
- begin
- result := FFilter.FState = State_Stopped;
- end;
-
- // NewSegment notifies of the start/stop/rate applying to the data
- // about to be received. Default implementation records data and
- // returns S_OK.
- // Override this to pass downstream.
-
- function TBCBasePin.NewSegment(tStart, tStop: TReferenceTime;
- dRate: double): HRESULT;
- begin
- FStart := tStart;
- FStop := tStop;
- FRate := dRate;
- result := S_OK;
- end;
-
- function TBCBasePin.NonDelegatingAddRef: Integer;
- begin
- ASSERT(InterlockedIncrement(FRef) > 0);
- result := FFilter._AddRef;
- end;
-
- function TBCBasePin.NonDelegatingRelease: Integer;
- begin
- ASSERT(InterlockedDecrement(FRef) >= 0);
- result := FFilter._Release
- end;
-
- function TBCBasePin.Notify(pSelf: IBaseFilter; q: TQuality): HRESULT;
- begin
- DbgLog(self, 'IQualityControl::Notify not over-ridden from CBasePin. (IGNORE is OK)');
- result := E_NOTIMPL;
- end;
-
- { Does this pin support this media type WARNING this interface function does
- not lock the main object as it is meant to be asynchronous by nature - if
- the media types you support depend on some internal state that is updated
- dynamically then you will need to implement locking in a derived class }
-
- function TBCBasePin.QueryAccept(const pmt: TAMMediaType): HRESULT;
- begin
- { The CheckMediaType method is valid to return error codes if the media
- type is horrible, an example might be E_INVALIDARG. What we do here
- is map all the error codes into either S_OK or S_FALSE regardless }
- result := CheckMediaType(@pmt);
- if FAILED(result) then result := S_FALSE;
- end;
-
- function TBCBasePin.QueryDirection(out pPinDir: TPinDirection): HRESULT;
- begin
- pPinDir := Fdir;
- result := NOERROR;
- end;
-
- function TBCBasePin.QueryId(out Id: PWideChar): HRESULT;
- begin
- result := AMGetWideString(FPinName, id);
- end;
-
- function TBCBasePin.QueryInternalConnections(out apPin: IPin;
- var nPin: ULONG): HRESULT;
- begin
- result := E_NOTIMPL;
- end;
-
- // Return information about the filter we are connect to
-
- function TBCBasePin.QueryPinInfo(out pInfo: TPinInfo): HRESULT;
- begin
- pInfo.pFilter := FFilter;
- if (FPinName <> '') then
- begin
- move(Pointer(FPinName)^, pInfo.achName, length(FPinName)*2);
- pInfo.achName[length(FPinName)] := #0;
- end
- else pInfo.achName[0] := #0;
- pInfo.dir := Fdir;
- result := NOERROR;
- end;
-
- { Called normally by an output pin on an input pin to try and establish a
- connection. }
-
- function TBCBasePin.ReceiveConnection(pConnector: IPin;
- const pmt: TAMMediaType): HRESULT;
- begin
- FLock.Lock;
- try
- // Are we already connected
- if (FConnected <> nil) then
- begin
- result := VFW_E_ALREADY_CONNECTED;
- exit;
- end;
-
- // See if the filter is active
- if (not IsStopped) and (not FCanReconnectWhenActive) then
- begin
- result := VFW_E_NOT_STOPPED;
- exit;
- end;
-
- result := CheckConnect(pConnector);
- if FAILED(result) then
- begin
- // Since the procedure is already returning an error code, there
- // is nothing else this function can do to report the error.
- ASSERT(SUCCEEDED(BreakConnect));
- exit;
- end;
-
- // Ask derived class if this media type is ok
-
- //CMediaType * pcmt = (CMediaType*) pmt;
- result := CheckMediaType(@pmt);
- if (result <> NOERROR) then
- begin
- // no -we don't support this media type
- // Since the procedure is already returning an error code, there
- // is nothing else this function can do to report the error.
- ASSERT(SUCCEEDED(BreakConnect));
- // return a specific media type error if there is one
- // or map a general failure code to something more helpful
- // (in particular S_FALSE gets changed to an error code)
- if (SUCCEEDED(result) or
- (result = E_FAIL) or
- (result = E_INVALIDARG)) then
- result := VFW_E_TYPE_NOT_ACCEPTED;
- exit;
- end;
-
- // Complete the connection
- FConnected := pConnector;
- result := SetMediaType(@pmt);
- if SUCCEEDED(result) then
- begin
- result := CompleteConnect(pConnector);
- if SUCCEEDED(result) then
- begin
- result := S_OK;
- exit;
- end;
- end;
-
- DbgLog(self, 'Failed to set the media type or failed to complete the connection.');
- FConnected := nil;
-
- // Since the procedure is already returning an error code, there
- // is nothing else this function can do to report the error.
- ASSERT(SUCCEEDED(BreakConnect));
- finally
- FLock.UnLock;
- end;
- end;
-
- { Called by IMediaFilter implementation when the state changes from
- to either paused to running and in derived classes could do things like
- commit memory and grab hardware resource (the default is to do nothing) }
-
- function TBCBasePin.Run(Start: TReferenceTime): HRESULT;
- begin
- result := NOERROR;
- end;
-
-
- function TBCBasePin.GetCurrentMediaType: TBCMediaType;
- begin
- result := TBCMediaType(@FMT);
- end;
-
- function TBCBasePin.GetAMMediaType: PAMMediaType;
- begin
- result := @FMT;
- end;
-
- { This is called to set the format for a pin connection - CheckMediaType
- will have been called to check the connection format and if it didn't
- return an error code then this (virtual) function will be invoked }
-
- function TBCBasePin.SetMediaType(mt: PAMMediaType): HRESULT;
- begin
- FreeMediaType(@Fmt);
- CopyMediaType(@Fmt, mt);
- result := NOERROR;
- end;
-
- function TBCBasePin.SetSink(piqc: IQualityControl): HRESULT;
- begin
- FLock.Lock;
- try
- FQSink := piqc;
- result := NOERROR;
- finally
- FLock.UnLock;
- end;
- end;
-
- { Given an enumerator we cycle through all the media types it proposes and
- firstly suggest them to our derived pin class and if that succeeds try
- them with the pin in a ReceiveConnection call. This means that if our pin
- proposes a media type we still check in here that we can support it. This
- is deliberate so that in simple cases the enumerator can hold all of the
- media types even if some of them are not really currently available }
-
- function TBCBasePin.TryMediaTypes(ReceivePin: IPin; pmt: PAMMediaType;
- Enum: IEnumMediaTypes): HRESULT;
- var
- MediaCount: Cardinal;
- hrFailure : HResult;
- MediaType : PAMMediaType;
- begin
- // Reset the current enumerator position
- result := Enum.Reset;
- if Failed(result) then exit;
-
- MediaCount := 0;
-
- // attempt to remember a specific error code if there is one
- hrFailure := S_OK;
-
- while true do
- begin
- { Retrieve the next media type NOTE each time round the loop the
- enumerator interface will allocate another AM_MEDIA_TYPE structure
- If we are successful then we copy it into our output object, if
- not then we must delete the memory allocated before returning }
-
- result := Enum.Next(1, MediaType, @MediaCount);
- if (result <> S_OK) then
- begin
- if (S_OK = hrFailure) then
- hrFailure := VFW_E_NO_ACCEPTABLE_TYPES;
- result := hrFailure;
- exit;
- end;
-
- ASSERT(MediaCount = 1);
- ASSERT(MediaType <> nil);
- // check that this matches the partial type (if any)
-
- if (pmt = nil) or TBCMediaType(MediaType).MatchesPartial(pmt) then
- begin
- result := AttemptConnection(ReceivePin, MediaType);
- // attempt to remember a specific error code
- if FAILED(result) and
- SUCCEEDED(hrFailure) and
- (result <> E_FAIL) and
- (result <> E_INVALIDARG) and
- (result <> VFW_E_TYPE_NOT_ACCEPTED) then hrFailure := result;
- end
- else result := VFW_E_NO_ACCEPTABLE_TYPES;
- DeleteMediaType(MediaType);
- if result = S_OK then exit;
- end;
- end;
-
- { TBCEnumMediaTypes }
-
- { The media types a filter supports can be quite dynamic so we add to
- the general IEnumXXXX interface the ability to be signaled when they
- change via an event handle the connected filter supplies. Until the
- Reset method is called after the state changes all further calls to
- the enumerator (except Reset) will return E_UNEXPECTED error code. }
-
- function TBCEnumMediaTypes.AreWeOutOfSync: boolean;
- begin
- if FPin.GetMediaTypeVersion = FVersion then result := FALSE else result := TRUE;
- end;
-
- { One of an enumerator's basic member functions allows us to create a cloned
- interface that initially has the same state. Since we are taking a snapshot
- of an object (current position and all) we must lock access at the start }
-
- function TBCEnumMediaTypes.Clone(out ppEnum: IEnumMediaTypes): HRESULT;
- begin
- result := NOERROR;
- // Check we are still in sync with the pin
- if AreWeOutOfSync then
- begin
- ppEnum := nil;
- result := VFW_E_ENUM_OUT_OF_SYNC;
- exit;
- end
- else
- begin
- ppEnum := TBCEnumMediaTypes.Create(FPin, self);
- if (ppEnum = nil) then result := E_OUTOFMEMORY;
- end;
- end;
-
- constructor TBCEnumMediaTypes.Create(Pin: TBCBasePin;
- EnumMediaTypes: TBCEnumMediaTypes);
- begin
- FPosition := 0;
- FPin := Pin;
- {$IFDEF DEBUG}
- DbgLog(nil, 'TBCEnumMediaTypes.Create');
- {$ENDIF}
-
- // We must be owned by a pin derived from CBasePin */
- ASSERT(Pin <> nil);
-
- // Hold a reference count on our pin
- FPin._AddRef;
-
- // Are we creating a new enumerator
-
- if (EnumMediaTypes = nil) then
- begin
- FVersion := FPin.GetMediaTypeVersion;
- exit;
- end;
-
- FPosition := EnumMediaTypes.FPosition;
- FVersion := EnumMediaTypes.FVersion;
- end;
-
- { Destructor releases the reference count on our base pin. NOTE since we hold
- a reference count on the pin who created us we know it is safe to release
- it, no access can be made to it afterwards though as we might have just
- caused the last reference count to go and the object to be deleted }
-
- destructor TBCEnumMediaTypes.Destroy;
- begin
- {$IFDEF DEBUG}
- DbgLog(nil, 'TBCEnumMediaTypes.Destroy');
- {$ENDIF}
- FPin._Release;
- inherited;
- end;
-
- { Enumerate the next pin(s) after the current position. The client using this
- interface passes in a pointer to an array of pointers each of which will
- be filled in with a pointer to a fully initialised media type format
- Return NOERROR if it all works,
- S_FALSE if fewer than cMediaTypes were enumerated.
- VFW_E_ENUM_OUT_OF_SYNC if the enumerator has been broken by
- state changes in the filter
- The actual count always correctly reflects the number of types in the array.}
-
- function TBCEnumMediaTypes.Next(cMediaTypes: ULONG;
- out ppMediaTypes: PAMMediaType; pcFetched: PULONG): HRESULT;
- type TMTDynArray = array of PAMMediaType;
- var
- Fetched: Cardinal;
- cmt: PAMMediaType;
- begin
- // Check we are still in sync with the pin
- if AreWeOutOfSync then
- begin
- result := VFW_E_ENUM_OUT_OF_SYNC;
- exit;
- end;
-
- if (pcFetched <> nil) then
- pcFetched^ := 0 // default unless we succeed
- // now check that the parameter is valid
- else
- if (cMediaTypes > 1) then
- begin // pcFetched == NULL
- result := E_INVALIDARG;
- exit;
- end;
-
- Fetched := 0; // increment as we get each one.
-
- { Return each media type by asking the filter for them in turn - If we
- have an error code retured to us while we are retrieving a media type
- we assume that our internal state is stale with respect to the filter
- (for example the window size changing) so we return
- VFW_E_ENUM_OUT_OF_SYNC }
-
- new(cmt);
- while (cMediaTypes > 0) do
- begin
- TBCMediaType(cmt).InitMediaType;
- inc(FPosition);
- result := FPin.GetMediaType(FPosition-1, cmt);
- if (S_OK <> result) then Break;
-
- { We now have a CMediaType object that contains the next media type
- but when we assign it to the array position we CANNOT just assign
- the AM_MEDIA_TYPE structure because as soon as the object goes out of
- scope it will delete the memory we have just copied. The function
- we use is CreateMediaType which allocates a task memory block }
-
- { Transfer across the format block manually to save an allocate
- and free on the format block and generally go faster }
-
- TMTDynArray(@ppMediaTypes)[Fetched] := CoTaskMemAlloc(sizeof(TAMMediaType));
- if TMTDynArray(@ppMediaTypes)[Fetched] = nil then Break;
-
- { Do a regular copy }
- //CopyMediaType(TMTDynArray(@ppMediaTypes)[Fetched], cmt);
- Move(cmt^,TMTDynArray(@ppMediaTypes)[Fetched]^,SizeOf(TAMMediaType));
-
- // Make sure the destructor doesn't free these
- cmt.pbFormat := nil;
- cmt.cbFormat := 0;
- Pointer(cmt.pUnk) := nil;
-
- inc(Fetched);
- dec(cMediaTypes);
- end;
- dispose(cmt);
- if (pcFetched <> nil) then pcFetched^ := Fetched;
- if cMediaTypes = 0 then result := NOERROR else result := S_FALSE;
- end;
-
- { Set the current position back to the start
- Reset has 3 simple steps:
- set position to head of list
- sync enumerator with object being enumerated
- return S_OK }
-
- function TBCEnumMediaTypes.Reset: HRESULT;
- begin
- FPosition := 0;
- // Bring the enumerator back into step with the current state. This
- // may be a noop but ensures that the enumerator will be valid on the
- // next call.
- FVersion := FPin.GetMediaTypeVersion;
- result := NOERROR;
- end;
-
- // Skip over one or more entries in the enumerator
-
- function TBCEnumMediaTypes.Skip(cMediaTypes: ULONG): HRESULT;
- var cmt: PAMMediaType;
- begin
- cmt := nil;
- // If we're skipping 0 elements we're guaranteed to skip the
- // correct number of elements
- if (cMediaTypes = 0) then
- begin
- result := S_OK;
- exit;
- end;
- // Check we are still in sync with the pin
- if AreWeOutOfSync then
- begin
- result := VFW_E_ENUM_OUT_OF_SYNC;
- exit;
- end;
-
- FPosition := FPosition + cMediaTypes;
-
- // See if we're over the end
- if (S_OK = FPin.GetMediaType(FPosition - 1, cmt)) then result := S_OK else result := S_FALSE;
- end;
-
- { TBCBaseOutputPin }
-
- // Commit the allocator's memory, this is called through IMediaFilter
- // which is responsible for locking the object before calling us
-
- function TBCBaseOutputPin.Active: HRESULT;
- begin
- if (FAllocator = nil) then
- result := VFW_E_NO_ALLOCATOR
- else result := FAllocator.Commit;
- end;
-
- function TBCBaseOutputPin.BeginFlush: HRESULT;
- begin
- result := E_UNEXPECTED;
- end;
-
- // Overriden from CBasePin
- function TBCBaseOutputPin.BreakConnect: HRESULT;
- begin
- // Release any allocator we hold
- if (FAllocator <> nil) then
- begin
- // Always decommit the allocator because a downstream filter may or
- // may not decommit the connection's allocator. A memory leak could
- // occur if the allocator is not decommited when a connection is broken.
- result := FAllocator.Decommit;
- if FAILED(result) then exit;
- FAllocator := nil;
- end;
-
- // Release any input pin interface we hold
- if (FInputPin <> nil) then FInputPin := nil;
- result := NOERROR;
- end;
-
- { This method is called when the output pin is about to try and connect to
- an input pin. It is at this point that you should try and grab any extra
- interfaces that you need, in this case IMemInputPin. Because this is
- only called if we are not currently connected we do NOT need to call
- BreakConnect. This also makes it easier to derive classes from us as
- BreakConnect is only called when we actually have to break a connection
- (or a partly made connection) and not when we are checking a connection }
-
- function TBCBaseOutputPin.CheckConnect(Pin: IPin): HRESULT;
- begin
- result := inherited CheckConnect(Pin);
- if FAILED(result) then exit;
-
- // get an input pin and an allocator interface
- result := Pin.QueryInterface(IID_IMemInputPin, FInputPin);
- if FAILED(result) then exit;
- result := NOERROR;
- end;
-
- // This is called after a media type has been proposed
- // Try to complete the connection by agreeing the allocator
- function TBCBaseOutputPin.CompleteConnect(ReceivePin: IPin): HRESULT;
- begin
- result := DecideAllocator(FInputPin, FAllocator);
- end;
-
- constructor TBCBaseOutputPin.Create(ObjectName: string;
- Filter: TBCBaseFilter; Lock: TBCCritSec; out hr: HRESULT;
- Name: WideString);
- begin
- inherited Create(ObjectName, Filter, Lock, hr, Name, PINDIR_OUTPUT);
- FAllocator := nil;
- FInputPin := nil;
- ASSERT(FFilter <> nil);
- end;
-
- { Decide on an allocator, override this if you want to use your own allocator
- Override DecideBufferSize to call SetProperties. If the input pin fails
- the GetAllocator call then this will construct a CMemAllocator and call
- DecideBufferSize on that, and if that fails then we are completely hosed.
- If the you succeed the DecideBufferSize call, we will notify the input
- pin of the selected allocator. NOTE this is called during Connect() which
- therefore looks after grabbing and locking the object's critical section }
-
- // We query the input pin for its requested properties and pass this to
- // DecideBufferSize to allow it to fulfill requests that it is happy
- // with (eg most people don't care about alignment and are thus happy to
- // use the downstream pin's alignment request).
-
- function TBCBaseOutputPin.DecideAllocator(Pin: IMemInputPin;
- out Alloc: IMemAllocator): HRESULT;
- var
- prop: TAllocatorProperties;
- begin
- Alloc := nil;
-
- // get downstream prop request
- // the derived class may modify this in DecideBufferSize, but
- // we assume that he will consistently modify it the same way,
- // so we only get it once
- ZeroMemory(@prop, sizeof(TAllocatorProperties));
-
- // whatever he returns, we assume prop is either all zeros
- // or he has filled it out.
- Pin.GetAllocatorRequirements(prop);
-
- // if he doesn't care about alignment, then set it to 1
- if (prop.cbAlign = 0) then prop.cbAlign := 1;
-
- // Try the allocator provided by the input pin
-
- result := Pin.GetAllocator(Alloc);
- if SUCCEEDED(result) then
- begin
- result := DecideBufferSize(Alloc, @prop);
- if SUCCEEDED(result) then
- begin
- result := Pin.NotifyAllocator(Alloc, FALSE);
- if SUCCEEDED(result) then
- begin
- result := NOERROR;
- exit;
- end;
- end;
- end;
-
- // If the GetAllocator failed we may not have an interface
-
- if (Alloc <> nil) then Alloc := nil;
-
- // Try the output pin's allocator by the same method
-
- result := InitAllocator(Alloc);
- if SUCCEEDED(result) then
- begin
- // note - the properties passed here are in the same
- // structure as above and may have been modified by
- // the previous call to DecideBufferSize
- result := DecideBufferSize(Alloc, @prop);
- if SUCCEEDED(result) then
- begin
- result := Pin.NotifyAllocator(Alloc, FALSE);
- if SUCCEEDED(result) then
- begin
- result := NOERROR;
- exit;
- end;
- end;
- end;
- // Likewise we may not have an interface to release
- if (Alloc <> nil) then Alloc := nil;
- end;
-
- function TBCBaseOutputPin.DecideBufferSize(Alloc: IMemAllocator;
- propInputRequest: PAllocatorProperties): HRESULT;
- begin
- result := S_OK; // ???
- end;
-
- { Deliver a filled-in sample to the connected input pin. NOTE the object must
- have locked itself before calling us otherwise we may get halfway through
- executing this method only to find the filter graph has got in and
- disconnected us from the input pin. If the filter has no worker threads
- then the lock is best applied on Receive(), otherwise it should be done
- when the worker thread is ready to deliver. There is a wee snag to worker
- threads that this shows up. The worker thread must lock the object when
- it is ready to deliver a sample, but it may have to wait until a state
- change has completed, but that may never complete because the state change
- is waiting for the worker thread to complete. The way to handle this is for
- the state change code to grab the critical section, then set an abort event
- for the worker thread, then release the critical section and wait for the
- worker thread to see the event we set and then signal that it has finished
- (with another event). At which point the state change code can complete }
-
- // note (if you've still got any breath left after reading that) that you
- // need to release the sample yourself after this call. if the connected
- // input pin needs to hold onto the sample beyond the call, it will addref
- // the sample itself.
-
- // of course you must release this one and call GetDeliveryBuffer for the
- // next. You cannot reuse it directly.
-
- function TBCBaseOutputPin.Deliver(Sample: IMediaSample): HRESULT;
- begin
- if (FInputPin = nil) then result := VFW_E_NOT_CONNECTED
- else result := FInputPin.Receive(Sample);
- end;
-
- // call BeginFlush on the connected input pin
- function TBCBaseOutputPin.DeliverBeginFlush: HRESULT;
- begin
- // remember this is on IPin not IMemInputPin
- if (FConnected = nil) then
- result := VFW_E_NOT_CONNECTED
- else result := FConnected.BeginFlush;
- end;
-
- // call EndFlush on the connected input pin
- function TBCBaseOutputPin.DeliverEndFlush: HRESULT;
- begin
- // remember this is on IPin not IMemInputPin
- if (FConnected = nil) then
- result := VFW_E_NOT_CONNECTED
- else result := FConnected.EndFlush;
- end;
-
- // called from elsewhere in our filter to pass EOS downstream to
- // our connected input pin
-
- function TBCBaseOutputPin.DeliverEndOfStream: HRESULT;
- begin
- // remember this is on IPin not IMemInputPin
- if (FConnected = nil) then
- result := VFW_E_NOT_CONNECTED
- else result := FConnected.EndOfStream;
- end;
-
- // deliver NewSegment to connected pin
- function TBCBaseOutputPin.DeliverNewSegment(Start, Stop: TReferenceTime;
- Rate: double): HRESULT;
- begin
- if (FConnected = nil) then
- result := VFW_E_NOT_CONNECTED
- else result := FConnected.NewSegment(Start, Stop, Rate);
- end;
-
- function TBCBaseOutputPin.EndFlush: HRESULT;
- begin
- result := E_UNEXPECTED;
- end;
-
- // we have a default handling of EndOfStream which is to return
- // an error, since this should be called on input pins only
- function TBCBaseOutputPin.EndOfStream: HRESULT;
- begin
- result := E_UNEXPECTED;
- end;
-
- // This returns an empty sample buffer from the allocator WARNING the same
- // dangers and restrictions apply here as described below for Deliver()
-
- function TBCBaseOutputPin.GetDeliveryBuffer(out Sample: IMediaSample;
- StartTime, EndTime: PReferenceTime; Flags: Longword): HRESULT;
- begin
- if (FAllocator <> nil) then
- result := FAllocator.GetBuffer(Sample, StartTime, EndTime, Flags)
- else result := E_NOINTERFACE;
- end;
-
- { Free up or unprepare allocator's memory, this is called through
- IMediaFilter which is responsible for locking the object first }
-
- function TBCBaseOutputPin.Inactive: HRESULT;
- begin
- FRunTimeError := FALSE;
- if (FAllocator = nil) then
- result := VFW_E_NO_ALLOCATOR
- else result := FAllocator.Decommit;
- end;
-
- // This is called when the input pin didn't give us a valid allocator
- function TBCBaseOutputPin.InitAllocator(out Alloc: IMemAllocator): HRESULT;
- begin
- result := CoCreateInstance(CLSID_MemoryAllocator, nil, CLSCTX_INPROC_SERVER,
- IID_IMemAllocator, Alloc);
- end;
-
- { TBCBaseInputPin }
-
- // Default handling for BeginFlush - call at the beginning
- // of your implementation (makes sure that all Receive calls
- // fail). After calling this, you need to free any queued data
- // and then call downstream.
-
- function TBCBaseInputPin.BeginFlush: HRESULT;
- begin
- // BeginFlush is NOT synchronized with streaming but is part of
- // a control action - hence we synchronize with the filter
- FLock.Lock;
- try
- // if we are already in mid-flush, this is probably a mistake
- // though not harmful - try to pick it up for now so I can think about it
- ASSERT(not FFlushing);
- // first thing to do is ensure that no further Receive calls succeed
- FFlushing := TRUE;
- // now discard any data and call downstream - must do that
- // in derived classes
- result := S_OK;
- finally
- FLock.UnLock;
- end;
-
- end;
-
- function TBCBaseInputPin.BreakConnect: HRESULT;
- begin
- // We don't need our allocator any more
- if (FAllocator <> nil) then
- begin
- // Always decommit the allocator because a downstream filter may or
- // may not decommit the connection's allocator. A memory leak could
- // occur if the allocator is not decommited when a pin is disconnected.
- result := FAllocator.Decommit;
- if FAILED(result) then exit;
- FAllocator := nil;
- end;
- result := S_OK;
- end;
-
- // Check if it's OK to process data
-
- function TBCBaseInputPin.CheckStreaming: HRESULT;
- begin
- // Shouldn't be able to get any data if we're not connected!
- ASSERT(IsConnected);
- // Don't process stuff in Stopped state
- if IsStopped then begin result := VFW_E_WRONG_STATE; exit end;
- if FFlushing then begin result := S_FALSE; exit end;
- if FRunTimeError then begin result := VFW_E_RUNTIME_ERROR; exit end;
- result := S_OK;
- end;
-
- // Constructor creates a default allocator object
-
- constructor TBCBaseInputPin.Create(ObjectName: string;
- Filter: TBCBaseFilter; Lock: TBCCritSec; out hr: HRESULT;
- Name: WideString);
- begin
- inherited create(ObjectName, Filter, Lock, hr, Name, PINDIR_INPUT);
- FAllocator := nil;
- FReadOnly := false;
- FFlushing := false;
- ZeroMemory(@FSampleProps, sizeof(FSampleProps));
- end;
-
- destructor TBCBaseInputPin.Destroy;
- begin
- if FAllocator <> nil then FAllocator := nil;
- inherited;
- end;
-
- // default handling for EndFlush - call at end of your implementation
- // - before calling this, ensure that there is no queued data and no thread
- // pushing any more without a further receive, then call downstream,
- // then call this method to clear the m_bFlushing flag and re-enable
- // receives
-
- function TBCBaseInputPin.EndFlush: HRESULT;
- begin
- // Endlush is NOT synchronized with streaming but is part of
- // a control action - hence we synchronize with the filter
- FLock.Lock;
- try
- // almost certainly a mistake if we are not in mid-flush
- ASSERT(FFlushing);
- // before calling, sync with pushing thread and ensure
- // no more data is going downstream, then call EndFlush on
- // downstream pins.
- // now re-enable Receives
- FFlushing := FALSE;
- // No more errors
- FRunTimeError := FALSE;
- result := S_OK;
- finally
- FLock.UnLock;
- end;
- end;
-
- { Return the allocator interface that this input pin would like the output
- pin to use. NOTE subsequent calls to GetAllocator should all return an
- interface onto the SAME object so we create one object at the start
-
- Note:
- The allocator is Release()'d on disconnect and replaced on
- NotifyAllocator().
-
- Override this to provide your own allocator.}
- function TBCBaseInputPin.GetAllocator(
- out ppAllocator: IMemAllocator): HRESULT;
- begin
- FLock.Lock;
- try
- if (FAllocator = nil) then
- begin
- result := CoCreateInstance(CLSID_MemoryAllocator, nil, CLSCTX_INPROC_SERVER,
- IID_IMemAllocator, FAllocator);
- if FAILED(result) then exit;
- end;
- ASSERT(FAllocator <> nil);
- ppAllocator := FAllocator;
- result := NOERROR;
- finally
- FLock.UnLock;
- end;
- end;
-
- // what requirements do we have of the allocator - override if you want
- // to support other people's allocators but need a specific alignment
- // or prefix.
-
- function TBCBaseInputPin.GetAllocatorRequirements(
- out pProps: TAllocatorProperties): HRESULT;
- begin
- result := E_NOTIMPL;
- end;
-
- { Free up or unprepare allocator's memory, this is called through
- IMediaFilter which is responsible for locking the object first. }
-
- function TBCBaseInputPin.Inactive: HRESULT;
- begin
- FRunTimeError := FALSE;
- if (FAllocator = nil) then
- begin
- result := VFW_E_NO_ALLOCATOR;
- exit;
- end;
- FFlushing := FALSE;
- result := FAllocator.Decommit;
- end;
-
- function TBCBaseInputPin.Notify(pSelf: IBaseFilter; q: TQuality): HRESULT;
- begin
- DbgLog(self, 'IQuality.Notify called on an input pin');
- result := NOERROR;
- end;
-
- { Tell the input pin which allocator the output pin is actually going to use
- Override this if you care - NOTE the locking we do both here and also in
- GetAllocator is unnecessary but derived classes that do something useful
- will undoubtedly have to lock the object so this might help remind people }
-
- function TBCBaseInputPin.NotifyAllocator(pAllocator: IMemAllocator;
- bReadOnly: BOOL): HRESULT;
- begin
- FLock.Lock;
- try
- FAllocator := pAllocator;
- // the readonly flag indicates whether samples from this allocator should
- // be regarded as readonly - if true, then inplace transforms will not be
- // allowed.
- FReadOnly := bReadOnly;
- result := NOERROR;
- finally
- FLock.UnLock;
- end;
- end;
-
- // Pass on the Quality notification q to
- // a. Our QualityControl sink (if we have one) or else
- // b. to our upstream filter
- // and if that doesn't work, throw it away with a bad return code
-
- function TBCBaseInputPin.PassNotify(const q: TQuality): HRESULT;
- var IQC: IQualityControl;
- begin
- // We pass the message on, which means that we find the quality sink
- // for our input pin and send it there
-
- DbgLog(self, 'Passing Quality notification through transform');
- if (FQSink <> nil) then
- begin
- result := FQSink.Notify(FFilter, q);
- exit;
- end
- else
- begin
- // no sink set, so pass it upstream
- result := VFW_E_NOT_FOUND; // default
- if (FConnected <> nil) then
- begin
- FConnected.QueryInterface(IID_IQualityControl, IQC);
- if (IQC <> nil) then
- begin
- result := IQC.Notify(FFilter, q);
- IQC := nil;
- end;
- end;
- end;
- end;
-
- { Do something with this media sample - this base class checks to see if the
- format has changed with this media sample and if so checks that the filter
- will accept it, generating a run time error if not. Once we have raised a
- run time error we set a flag so that no more samples will be accepted
- It is important that any filter should override this method and implement
- synchronization so that samples are not processed when the pin is
- disconnected etc. }
-
- function TBCBaseInputPin.Receive(pSample: IMediaSample): HRESULT;
- var Sample2: IMediaSample2;
- begin
- ASSERT(pSample <> nil);
-
- result := CheckStreaming;
- if (S_OK <> result) then exit;
-
- // Check for IMediaSample2
- if SUCCEEDED(pSample.QueryInterface(IID_IMediaSample2, Sample2)) then
- begin
- result := Sample2.GetProperties(sizeof(FSampleProps), FSampleProps);
- Sample2 := nil;
- if FAILED(result) then exit;
- end
- else
- begin
- // Get the properties the hard way
- FSampleProps.cbData := sizeof(FSampleProps);
- FSampleProps.dwTypeSpecificFlags := 0;
- FSampleProps.dwStreamId := AM_STREAM_MEDIA;
- FSampleProps.dwSampleFlags := 0;
- if (S_OK = pSample.IsDiscontinuity) then
- FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_DATADISCONTINUITY;
- if (S_OK = pSample.IsPreroll) then
- FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_PREROLL;
- if (S_OK = pSample.IsSyncPoint) then
- FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_SPLICEPOINT;
- if SUCCEEDED(pSample.GetTime(FSampleProps.tStart, FSampleProps.tStop)) then
- FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_TIMEVALID or AM_SAMPLE_STOPVALID;
- if (S_OK = pSample.GetMediaType(FSampleProps.pMediaType)) then
- FSampleProps.dwSampleFlags := FSampleProps.dwSampleFlags or AM_SAMPLE_TYPECHANGED;
- pSample.GetPointer(PByte(FSampleProps.pbBuffer));
- FSampleProps.lActual := pSample.GetActualDataLength;
- FSampleProps.cbBuffer := pSample.GetSize;
- end;
-
- // Has the format changed in this sample
-
- if (not BOOL(FSampleProps.dwSampleFlags and AM_SAMPLE_TYPECHANGED)) then
- begin
- result := NOERROR;
- exit;
- end;
-
- // Check the derived class accepts this format */
- // This shouldn't fail as the source must call QueryAccept first */
-
- result := CheckMediaType(FSampleProps.pMediaType);
-
- if (result = NOERROR) then exit;
-
- // Raise a runtime error if we fail the media type
-
- FRunTimeError := TRUE;
- EndOfStream;
- FFilter.NotifyEvent(EC_ERRORABORT,VFW_E_TYPE_NOT_ACCEPTED,0);
- result := VFW_E_INVALIDMEDIATYPE;
- end;
-
- // See if Receive() might block
-
- function TBCBaseInputPin.ReceiveCanBlock: HRESULT;
- var
- c, Pins, OutputPins: Integer;
- Pin: TBCBasePin;
- pd: TPinDirection;
- Connected: IPin;
- InputPin: IMemInputPin;
- begin
- { Ask all the output pins if they block
- If there are no output pin assume we do block. }
- Pins := FFilter.GetPinCount;
- OutputPins := 0;
- for c := 0 to Pins - 1 do
- begin
- Pin := FFilter.GetPin(c);
- result := Pin.QueryDirection(pd);
- if FAILED(result) then exit;
- if (pd = PINDIR_OUTPUT) then
- begin
- result := Pin.ConnectedTo(Connected);
- if SUCCEEDED(result) then
- begin
- assert(Connected <> nil);
- inc(OutputPins);
- result := Connected.QueryInterface(IID_IMemInputPin, InputPin);
- Connected := nil;
- if SUCCEEDED(result) then
- begin
- result := InputPin.ReceiveCanBlock;
- InputPin := nil;
- if (result <> S_FALSE) then
- begin
- result := S_OK;
- exit;
- end;
- end
- else
- begin
- // There's a transport we don't understand here
- result := S_OK;
- exit;
- end;
- end;
- end;
- end;
- if OutputPins = 0 then result := S_OK else result := S_FALSE;
- end;
-
- // Receive multiple samples
-
- function TBCBaseInputPin.ReceiveMultiple(var pSamples: IMediaSample;
- nSamples: Integer; out nSamplesProcessed: Integer): HRESULT;
- type
- TMediaSampleDynArray = array of IMediaSample;
- begin
- result := S_OK;
- nSamplesProcessed := 0;
- dec(nSamples);
- while (nSamples >= 0) do
- begin
- result := Receive(TMediaSampleDynArray(@pSamples)[nSamplesProcessed]);
- // S_FALSE means don't send any more
- if (result <> S_OK) then break;
- inc(nSamplesProcessed);
- dec(nSamples)
- end;
- end;
-
- function TBCBaseInputPin.SampleProps: PAMSample2Properties;
- begin
- ASSERT(FSampleProps.cbData <> 0);
- result := @FSampleProps;
- end;
-
- { TBCTransformInputPin }
-
- // enter flushing state. Call default handler to block Receives, then
- // pass to overridable method in filter
-
- function TBCTransformInputPin.BeginFlush: HRESULT;
- begin
- FTransformFilter.FcsFilter.Lock;
- try
- // Are we actually doing anything?
- ASSERT(FTransformFilter.FOutput <> nil);
- if ((not IsConnected) or (not FTransformFilter.FOutput.IsConnected)) then
- begin
- result := VFW_E_NOT_CONNECTED;
- exit;
- end;
- result := inherited BeginFlush;
- if FAILED(result) then exit;
- result := FTransformFilter.BeginFlush;
- finally
- FTransformFilter.FcsFilter.UnLock;
- end;
- end;
-
- // provides derived filter a chance to release it's extra interfaces
-
- function TBCTransformInputPin.BreakConnect: HRESULT;
- begin
- ASSERT(IsStopped);
- FTransformFilter.BreakConnect(PINDIR_INPUT);
- result := inherited BreakConnect;
- end;
-
- function TBCTransformInputPin.CheckConnect(Pin: IPin): HRESULT;
- begin
- result := FTransformFilter.CheckConnect(PINDIR_INPUT, Pin);
- if FAILED(result) then exit;
- result := inherited CheckConnect(Pin);
- end;
-
- // check that we can support a given media type
-
- function TBCTransformInputPin.CheckMediaType(
- mtIn: PAMMediaType): HRESULT;
- begin
- // Check the input type
- result := FTransformFilter.CheckInputType(mtIn);
- if (S_OK <> result) then exit;
- // if the output pin is still connected, then we have
- // to check the transform not just the input format
- if ((FTransformFilter.FOutput <> nil) and
- (FTransformFilter.FOutput.IsConnected)) then
- begin
- result := FTransformFilter.CheckTransform(mtIn,
- FTransformFilter.FOutput.AMMediaType);
- end;
- end;
-
- function TBCTransformInputPin.CheckStreaming: HRESULT;
- begin
- ASSERT(FTransformFilter.FOutput <> nil);
- if(not FTransformFilter.FOutput.IsConnected) then
- begin
- result := VFW_E_NOT_CONNECTED;
- exit;
- end
- else
- begin
- // Shouldn't be able to get any data if we're not connected!
- ASSERT(IsConnected);
- // we're flushing
- if FFlushing then
- begin
- result := S_FALSE;
- exit;
- end;
- // Don't process stuff in Stopped state
- if IsStopped then
- begin
- result := VFW_E_WRONG_STATE;
- exit;
- end;
- if FRunTimeError then
- begin
- result := VFW_E_RUNTIME_ERROR;
- exit;
- end;
- result := S_OK;
- end;
- end;
-
- function TBCTransformInputPin.CompleteConnect(ReceivePin: IPin): HRESULT;
- begin
- result := FTransformFilter.CompleteConnect(PINDIR_INPUT, ReceivePin);
- if FAILED(result) then exit;
- result := inherited CompleteConnect(ReceivePin);
- end;
-
- constructor TBCTransformInputPin.Create(ObjectName: string;
- TransformFilter: TBCTransformFilter; out hr: HRESULT; Name: WideString);
- begin
- inherited Create(ObjectName, TransformFilter, TransformFilter.FcsFilter, hr, Name);
- DbgLog(self, 'TBCTransformInputPin.Create');
- FTransformFilter := TransformFilter;
- end;
-
- // leave flushing state.
- // Pass to overridable method in filter, then call base class
- // to unblock receives (finally)
-
- destructor TBCTransformInputPin.destroy;
- begin
- DbgLog(self, 'TBCTransformInputPin.destroy');
- inherited;
- end;
-
- function TBCTransformInputPin.EndFlush: HRESULT;
- begin
- FTransformFilter.FcsFilter.Lock;
- try
- // Are we actually doing anything?
- ASSERT(FTransformFilter.FOutput <> nil);
- if((not IsConnected) or (not FTransformFilter.FOutput.IsConnected)) then
- begin
- result := VFW_E_NOT_CONNECTED;
- exit;
- end;
-
- result := FTransformFilter.EndFlush;
- if FAILED(result) then exit;
- result := inherited EndFlush;
- finally
- FTransformFilter.FcsFilter.UnLock;
- end;
- end;
-
- // provide EndOfStream that passes straight downstream
- // (there is no queued data)
-
- function TBCTransformInputPin.EndOfStream: HRESULT;
- begin
- FTransformFilter.FcsReceive.Lock;
- try
- result := CheckStreaming;
- if (S_OK = result) then
- result := FTransformFilter.EndOfStream;
- finally
- FTransformFilter.FcsReceive.UnLock;
- end;
- end;
-
- function TBCTransformInputPin.NewSegment(Start, Stop: TReferenceTime;
- Rate: double): HRESULT;
- begin
- // Save the values in the pin
- inherited NewSegment(Start, Stop, Rate);
- result := FTransformFilter.NewSegment(Start, Stop, Rate);
- end;
-
- function TBCTransformInputPin.QueryId(out id: PWideChar): HRESULT;
- begin
- AMGetWideString('In', Id);
- if id <> nil then result := S_OK else result := S_FALSE;
- end;
-
- // here's the next block of data from the stream.
- // AddRef it yourself if you need to hold it beyond the end
- // of this call.
-
- function TBCTransformInputPin.Receive(pSample: IMediaSample): HRESULT;
- begin
- FTransformFilter.FcsReceive.Lock;
- try
- ASSERT(pSample <> nil);
- // check all is well with the base class
- result := inherited Receive(pSample);
- if (result = S_OK) then
- result := FTransformFilter.Receive(pSample);
- finally
- FTransformFilter.FcsReceive.Unlock;
- end;
- end;
-
- // set the media type for this connection
-
- function TBCTransformInputPin.SetMediaType(mt: PAMMediaType): HRESULT;
- begin
- // Set the base class media type (should always succeed)
- result := inherited SetMediaType(mt);
- if FAILED(result) then exit;
- // check the transform can be done (should always succeed)
- ASSERT(SUCCEEDED(FTransformFilter.CheckInputType(mt)));
- result := FTransformFilter.SetMediaType(PINDIR_INPUT,mt);
- end;
-
- { TBCCritSec }
-
- constructor TBCCritSec.Create;
- begin
- InitializeCriticalSection(FCritSec);
- {$IFDEF DEBUG}
- FcurrentOwner := 0;
- FlockCount := 0;
- {$IFDEF TRACE}
- FTrace := TRUE;
- {$ELSE}
- FTrace := FALSE;
- {$ENDIF}
- {$ENDIF}
- end;
-
- function TBCCritSec.CritCheckIn: boolean;
- begin
- {$IFDEF DEBUG}
- result := (GetCurrentThreadId = Self.FcurrentOwner);
- {$ELSE}
- result := true;
- {$ENDIF}
- end;
-
- function TBCCritSec.CritCheckOut: boolean;
- begin
- {$IFDEF DEBUG}
- result := (GetCurrentThreadId <> Self.FcurrentOwner);
- {$ELSE}
- result := false;
- {$ENDIF}
- end;
-
- destructor TBCCritSec.Destroy;
- begin
- DeleteCriticalSection(FCritSec)
- end;
-
- procedure TBCCritSec.Lock;
- begin
- {$IFDEF DEBUG}
- if ((FCurrentOwner <> 0) and (FCurrentOwner <> GetCurrentThreadId)) then
- begin
- // already owned, but not by us
- if FTrace then
- begin
- DbgLog(nil, format('Thread %d about to wait for lock %x owned by %d',
- [GetCurrentThreadId, longint(self), FCurrentOwner]));
- end;
- end;
- {$ENDIF}
- EnterCriticalSection(FCritSec);
- {$IFDEF DEBUG}
- inc(FLockCount);
- if (FLockCount > 0) then
- begin
- // we now own it for the first time. Set owner information
- FcurrentOwner := GetCurrentThreadId;
- if FTrace then
- DbgLog(nil, format('Thread %d now owns lock %x', [FcurrentOwner, LongInt(self)]));
- end;
- {$ENDIF}
- end;
-
- procedure TBCCritSec.UnLock;
- begin
- {$IFDEF DEBUG}
- dec(FlockCount);
- if(FlockCount = 0) then
- begin
- // about to be unowned
- if FTrace then
- DbgLog(nil, format('Thread %d releasing lock %x', [FcurrentOwner, LongInt(Self)]));
- FcurrentOwner := 0;
- end;
- {$ENDIF}
- LeaveCriticalSection(FCritSec)
- end;
-
- { TBCTransformFilter }
-
- // Return S_FALSE to mean "pass the note on upstream"
- // Return NOERROR (Same as S_OK)
- // to mean "I've done something about it, don't pass it on"
-
- function TBCTransformFilter.AlterQuality(const q: TQuality): HRESULT;
- begin
- result := S_FALSE;
- end;
-
- // enter flush state. Receives already blocked
- // must override this if you have queued data or a worker thread
-
- function TBCTransformFilter.BeginFlush: HRESULT;
- begin
- result := NOERROR;
- if (FOutput <> nil) then
- // block receives -- done by caller (CBaseInputPin::BeginFlush)
- // discard queued data -- we have no queued data
- // free anyone blocked on receive - not possible in this filter
- // call downstream
- result := FOutput.DeliverBeginFlush;
- end;
-
- function TBCTransformFilter.BreakConnect(dir: TPinDirection): HRESULT;
- begin
- result := NOERROR;
- end;
-
- function TBCTransformFilter.CheckConnect(dir: TPinDirection;
- Pin: IPin): HRESULT;
- begin
- result := NOERROR;
- end;
-
- function TBCTransformFilter.CompleteConnect(direction: TPinDirection;
- ReceivePin: IPin): HRESULT;
- begin
- result := NOERROR;
- end;
-
- constructor TBCTransformFilter.Create(ObjectName: string; unk: IUnKnown;
- const clsid: TGUID);
- begin
- FcsFilter := TBCCritSec.Create;
- FcsReceive := TBCCritSec.Create;
- inherited Create(ObjectName,Unk,FcsFilter, clsid);
- FInput := nil;
- FOutput := nil;
- FEOSDelivered := FALSE;
- FQualityChanged:= FALSE;
- FSampleSkipped := FALSE;
- {$ifdef PERF}
- RegisterPerfId;
- {$endif}
- end;
-
- constructor TBCTransformFilter.CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown);
- begin
- Create(Factory.FName, Controller, Factory.FClassID);
- end;
-
- destructor TBCTransformFilter.destroy;
- begin
- if FInput <> nil then FInput.Free;
- if FOutput <> nil then FOutput.Free;
- DbgLog(self, 'TBCTransformFilter.destroy');
- FcsReceive.Free;
- inherited;
- end;
-
- // leave flush state. must override this if you have queued data
- // or a worker thread
-
- function TBCTransformFilter.EndFlush: HRESULT;
- begin
- // sync with pushing thread -- we have no worker thread
- // ensure no more data to go downstream -- we have no queued data
- // call EndFlush on downstream pins
- ASSERT(FOutput <> nil);
- result := FOutput.DeliverEndFlush;
- // caller (the input pin's method) will unblock Receives
- end;
-
- // EndOfStream received. Default behaviour is to deliver straight
- // downstream, since we have no queued data. If you overrode Receive
- // and have queue data, then you need to handle this and deliver EOS after
- // all queued data is sent
-
- function TBCTransformFilter.EndOfStream: HRESULT;
- begin
- result := NOERROR;
- if (FOutput <> nil) then
- result := FOutput.DeliverEndOfStream;
- end;
-
- // If Id is In or Out then return the IPin* for that pin
- // creating the pin if need be. Otherwise return NULL with an error.
-
- function TBCTransformFilter.FindPin(Id: PWideChar; out ppPin: IPin): HRESULT;
- begin
- if(WideString(Id) = 'In') then ppPin := GetPin(0) else
- if(WideString(Id) = 'Out') then ppPin := GetPin(1) else
- begin
- ppPin := nil;
- result := VFW_E_NOT_FOUND;
- exit;
- end;
-
- result := NOERROR;
- if(ppPin = nil) then result := E_OUTOFMEMORY;
- end;
-
- // return a non-addrefed CBasePin * for the user to addref if he holds onto it
- // for longer than his pointer to us. We create the pins dynamically when they
- // are asked for rather than in the constructor. This is because we want to
- // give the derived class an oppportunity to return different pin objects
-
- // We return the objects as and when they are needed. If either of these fails
- // then we return NULL, the assumption being that the caller will realise the
- // whole deal is off and destroy us - which in turn will delete everything.
-
- function TBCTransformFilter.GetPin(n: integer): TBCBasePin;
- var hr: HRESULT;
- begin
- hr := S_OK;
- // Create an input pin if necessary
- if(FInput = nil) then
- begin
- FInput := TBCTransformInputPin.Create('Transform input pin',
- self, // Owner filter
- hr, // Result code
- 'XForm In'); // Pin name
-
- // Can't fail
- ASSERT(SUCCEEDED(hr));
- if(FInput = nil) then
- begin
- result := nil;
- exit;
- end;
- FOutput := TBCTransformOutputPin.Create('Transform output pin',
- self, // Owner filter
- hr, // Result code
- 'XForm Out'); // Pin name
-
- // Can't fail
- ASSERT(SUCCEEDED(hr));
- if(FOutput = nil) then FreeAndNil(FInput);
- end;
-
- // Return the appropriate pin
-
- case n of
- 0 : result := FInput;
- 1 : result := FOutput;
- else
- result := nil;
- end;
- end;
-
- function TBCTransformFilter.GetPinCount: integer;
- begin
- result := 2;
- end;
-
- // Set up our output sample
-
- function TBCTransformFilter.InitializeOutputSample(Sample: IMediaSample;
- out OutSample: IMediaSample): HRESULT;
- var
- Props: PAMSample2Properties;
- Flags: DWORD;
- Start, Stop: PReferenceTime;
- OutSample2: IMediaSample2;
- OutProps: TAMSample2Properties;
- MediaStart, MediaEnd: Int64;
- begin
- // default - times are the same
-
- Props := FInput.SampleProps;
- if FSampleSkipped then Flags := AM_GBF_PREVFRAMESKIPPED else Flags := 0;
-
- // This will prevent the image renderer from switching us to DirectDraw
- // when we can't do it without skipping frames because we're not on a
- // keyframe. If it really has to switch us, it still will, but then we
- // will have to wait for the next keyframe
- if(not BOOL(Props.dwSampleFlags and AM_SAMPLE_SPLICEPOINT)) then Flags := Flags or AM_GBF_NOTASYNCPOINT;
-
- ASSERT(FOutput.FAllocator <> nil);
- if BOOL(Props.dwSampleFlags and AM_SAMPLE_TIMEVALID) then Start := @Props.tStart else Start := nil;
- if BOOL(Props.dwSampleFlags and AM_SAMPLE_STOPVALID) then Stop := @Props.tStop else Stop := nil;
- result := FOutput.FAllocator.GetBuffer(OutSample, Start, Stop, Flags);
- if FAILED(result) then exit;
- ASSERT(OutSample <> nil);
- if SUCCEEDED(OutSample.QueryInterface(IID_IMediaSample2, OutSample2)) then
- begin
- ASSERT(SUCCEEDED(OutSample2.GetProperties(4*4, OutProps)));
- OutProps.dwTypeSpecificFlags := Props.dwTypeSpecificFlags;
- OutProps.dwSampleFlags := (OutProps.dwSampleFlags and AM_SAMPLE_TYPECHANGED) or
- (Props.dwSampleFlags and (not AM_SAMPLE_TYPECHANGED));
-
- OutProps.tStart := Props.tStart;
- OutProps.tStop := Props.tStop;
- OutProps.cbData := (4*4) + (2*8);
-
- OutSample2.SetProperties((4*4)+(2*8), OutProps);
- if BOOL(Props.dwSampleFlags and AM_SAMPLE_DATADISCONTINUITY) then FSampleSkipped := FALSE;
- OutSample2 := nil;
- end
- else
- begin
- if BOOL(Props.dwSampleFlags and AM_SAMPLE_TIMEVALID) then
- OutSample.SetTime(@Props.tStart, @Props.tStop);
- if BOOL(Props.dwSampleFlags and AM_SAMPLE_SPLICEPOINT) then
- OutSample.SetSyncPoint(TRUE);
- if BOOL(Props.dwSampleFlags and AM_SAMPLE_DATADISCONTINUITY) then
- begin
- OutSample.SetDiscontinuity(TRUE);
- FSampleSkipped := FALSE;
- end;
- // Copy the media times
- if (Sample.GetMediaTime(MediaStart,MediaEnd) = NOERROR) then
- OutSample.SetMediaTime(@MediaStart, @MediaEnd);
- end;
- result := S_OK;
- end;
-
- function TBCTransformFilter.NewSegment(Start, Stop: TReferenceTime;
- Rate: double): HRESULT;
- begin
- result := S_OK;
- if (FOutput <> nil) then
- result := FOutput.DeliverNewSegment(Start, Stop, Rate);
- end;
-
- function TBCTransformFilter.Pause: HRESULT;
- begin
- FcsFilter.Lock;
- try
- result := NOERROR;
- if (FState = State_Paused) then
- begin
- // (This space left deliberately blank)
- end
- // If we have no input pin or it isn't yet connected then when we are
- // asked to pause we deliver an end of stream to the downstream filter.
- // This makes sure that it doesn't sit there forever waiting for
- // samples which we cannot ever deliver without an input connection.
-
- else
- if ((FInput = nil) or (FInput.IsConnected = FALSE)) then
- begin
- if ((FOutput <> nil) and (FEOSDelivered = FALSE)) then
- begin
- FOutput.DeliverEndOfStream;
- FEOSDelivered := TRUE;
- end;
- FState := State_Paused;
- end
-
- // We may have an input connection but no output connection
- // However, if we have an input pin we do have an output pin
-
- else
- if (FOutput.IsConnected = FALSE) then
- FState := State_Paused
- else
- begin
- if(FState = State_Stopped) then
- begin
- // allow a class derived from CTransformFilter
- // to know about starting and stopping streaming
- FcsReceive.Lock;
- try
- result := StartStreaming;
- finally
- FcsReceive.UnLock;
- end;
- end;
- if SUCCEEDED(result) then result := inherited Pause;
- end;
- FSampleSkipped := FALSE;
- FQualityChanged := FALSE;
- finally
- FcsFilter.UnLock;
- end;
- end;
-
- // override this to customize the transform process
-
- function TBCTransformFilter.Receive(Sample: IMediaSample): HRESULT;
- var
- Props: PAMSample2Properties;
- OutSample: IMediaSample;
- begin
- // Check for other streams and pass them on
- Props := FInput.SampleProps;
- if(Props.dwStreamId <> AM_STREAM_MEDIA) then
- begin
- result := FOutput.FInputPin.Receive(Sample);
- exit;
- end;
- // If no output to deliver to then no point sending us data
- ASSERT(FOutput <> nil) ;
- // Set up the output sample
- result := InitializeOutputSample(Sample, OutSample);
- if FAILED(result) then exit;
- result := Transform(Sample, OutSample);
- if FAILED(result) then
- begin
- DbgLog(self, 'Error from transform');
- exit;
- end
- else
- begin
- // the Transform() function can return S_FALSE to indicate that the
- // sample should not be delivered; we only deliver the sample if it's
- // really S_OK (same as NOERROR, of course.)
- if (result = NOERROR) then
- begin
- result := FOutput.FInputPin.Receive(OutSample);
- FSampleSkipped := FALSE; // last thing no longer dropped
- end
- else
- begin
- // S_FALSE returned from Transform is a PRIVATE agreement
- // We should return NOERROR from Receive() in this cause because returning S_FALSE
- // from Receive() means that this is the end of the stream and no more data should
- // be sent.
- if (result = S_FALSE) then
- begin
- // Release the sample before calling notify to avoid
- // deadlocks if the sample holds a lock on the system
- // such as DirectDraw buffers do
- OutSample := nil;
- FSampleSkipped := TRUE;
- if not FQualityChanged then
- begin
- NotifyEvent(EC_QUALITY_CHANGE,0,0);
- FQualityChanged := TRUE;
- end;
- result := NOERROR;
- exit;
- end;
- end;
- end;
- // release the output buffer. If the connected pin still needs it,
- // it will have addrefed it itself.
- OutSample := nil;
- end;
-
- function TBCTransformFilter.SetMediaType(direction: TPinDirection;
- pmt: PAMMediaType): HRESULT;
- begin
- result := NOERROR;
- end;
-
- // override these two functions if you want to inform something
- // about entry to or exit from streaming state.
-
- function TBCTransformFilter.StartStreaming: HRESULT;
- begin
- result := NOERROR;
- end;
-
- // override these so that the derived filter can catch them
-
- function TBCTransformFilter.Stop: HRESULT;
- begin
- FcsFilter.Lock;
- try
- if(FState = State_Stopped) then
- begin
- result := NOERROR;
- exit;
- end;
- // Succeed the Stop if we are not completely connected
- ASSERT((FInput = nil) or (FOutput <> nil));
- if((FInput = nil) or (FInput.IsConnected = FALSE) or (FOutput.IsConnected = FALSE)) then
- begin
- FState := State_Stopped;
- FEOSDelivered := FALSE;
- result := NOERROR;
- exit;
- end;
- ASSERT(FInput <> nil);
- ASSERT(FOutput <> nil);
- // decommit the input pin before locking or we can deadlock
- FInput.Inactive;
- // synchronize with Receive calls
- FcsReceive.Lock;
- try
- FOutput.Inactive;
- // allow a class derived from CTransformFilter
- // to know about starting and stopping streaming
- result := StopStreaming;
- if SUCCEEDED(result) then
- begin
- // complete the state transition
- FState := State_Stopped;
- FEOSDelivered := FALSE;
- end;
- finally
- FcsReceive.UnLock;
- end;
- finally
- FcsFilter.UnLock;
- end;
- end;
-
- function TBCTransformFilter.StopStreaming: HRESULT;
- begin
- result := NOERROR;
- end;
-
- function TBCTransformFilter.Transform(msIn, msout: IMediaSample): HRESULT;
- begin
- DbgLog(self, 'TBCTransformFilter.Transform should never be called');
- result := E_UNEXPECTED;
- end;
-
- { TBCTransformOutputPin }
-
- // provides derived filter a chance to release it's extra interfaces
-
- function TBCTransformOutputPin.BreakConnect: HRESULT;
- begin
- // Can't disconnect unless stopped
- ASSERT(IsStopped);
- FTransformFilter.BreakConnect(PINDIR_OUTPUT);
- result := inherited BreakConnect;
- end;
-
- // provides derived filter a chance to grab extra interfaces
-
- function TBCTransformOutputPin.CheckConnect(Pin: IPin): HRESULT;
- begin
- // we should have an input connection first
- ASSERT(FTransformFilter.FInput <> nil);
- if(FTransformFilter.FInput.IsConnected = FALSE) then
- begin
- result := E_UNEXPECTED;
- exit;
- end;
-
- result := FTransformFilter.CheckConnect(PINDIR_OUTPUT, Pin);
- if FAILED(result) then exit;
- result := inherited CheckConnect(Pin);
- end;
-
- // check a given transform - must have selected input type first
-
- function TBCTransformOutputPin.CheckMediaType(
- mtOut: PAMMediaType): HRESULT;
- begin
- // must have selected input first
- ASSERT(FTransformFilter.FInput <> nil);
- if(FTransformFilter.FInput.IsConnected = FALSE) then
- begin
- result := E_INVALIDARG;
- exit;
- end;
- result := FTransformFilter.CheckTransform(FTransformFilter.FInput.AMMediaType, mtOut);
- end;
-
- // Let derived class know when the output pin is connected
-
- function TBCTransformOutputPin.CompleteConnect(ReceivePin: IPin): HRESULT;
- begin
- result := FTransformFilter.CompleteConnect(PINDIR_OUTPUT, ReceivePin);
- if FAILED(result) then exit;
- result := inherited CompleteConnect(ReceivePin);
- end;
-
- constructor TBCTransformOutputPin.Create(ObjectName: string;
- TransformFilter: TBCTransformFilter; out hr: HRESULT; Name: WideString);
- begin
- inherited create(ObjectName, TransformFilter, TransformFilter.FcsFilter, hr, Name);
- FPosition := nil;
- DbgLog(self, 'TBCTransformOutputPin.Create');
- FTransformFilter := TransformFilter;
- end;
-
- function TBCTransformOutputPin.DecideBufferSize(Alloc: IMemAllocator;
- Prop: PAllocatorProperties): HRESULT;
- begin
- result := FTransformFilter.DecideBufferSize(Alloc, Prop);
- end;
-
- destructor TBCTransformOutputPin.destroy;
- begin
- DbgLog(self, 'TBCTransformOutputPin.Destroy');
- FPosition := nil;
- inherited;
- end;
-
- function TBCTransformOutputPin.GetMediaType(Position: integer;
- out MediaType: PAMMediaType): HRESULT;
- begin
- ASSERT(FTransformFilter.FInput <> nil);
- // We don't have any media types if our input is not connected
- if(FTransformFilter.FInput.IsConnected) then
- begin
- result := FTransformFilter.GetMediaType(Position, MediaType);
- exit;
- end
- else
- result := VFW_S_NO_MORE_ITEMS;
- end;
-
- function TBCTransformOutputPin.NonDelegatingQueryInterface(
- const IID: TGUID; out Obj): HResult;
- begin
- if IsEqualGUID(iid, IID_IMediaPosition) or IsEqualGUID(iid, IID_IMediaSeeking) then
- begin
- // we should have an input pin by now
- ASSERT(FTransformFilter.FInput <> nil);
- if (FPosition = nil) then
- begin
- result := CreatePosPassThru(GetOwner, FALSE, FTransformFilter.FInput, FPosition);
- if FAILED(result) then exit;
- end;
- result := FPosition.QueryInterface(iid, obj);
- end
- else
- result := inherited NonDelegatingQueryInterface(iid, obj);
- end;
-
- // Override this if you can do something constructive to act on the
- // quality message. Consider passing it upstream as well
-
- // Pass the quality mesage on upstream.
-
- function TBCTransformOutputPin.Notify(Sendr: IBaseFilter; q: TQuality): HRESULT;
- begin
- // First see if we want to handle this ourselves
- result := FTransformFilter.AlterQuality(q);
- if (result <> S_FALSE) then exit;
- // S_FALSE means we pass the message on.
- // Find the quality sink for our input pin and send it there
- ASSERT(FTransformFilter.FInput <> nil);
- result := FTransformFilter.FInput.PassNotify(q);
- end;
-
- function TBCTransformOutputPin.QueryId(out Id: PWideChar): HRESULT;
- begin
- result := AMGetWideString('Out', Id);
- end;
-
- // called after we have agreed a media type to actually set it in which case
- // we run the CheckTransform function to get the output format type again
-
- function TBCTransformOutputPin.SetMediaType(pmt: PAMMediaType): HRESULT;
- begin
- ASSERT(FTransformFilter.FInput <> nil);
- ASSERT(not IsEqualGUID(FTransformFilter.FInput.AMMediaType.majortype,GUID_NULL));
- // Set the base class media type (should always succeed)
- result := inherited SetMediaType(pmt);
- if FAILED(result) then exit;
- {$ifdef DEBUG}
- if(FAILED(FTransformFilter.CheckTransform(FTransformFilter.FInput.AMMediaType, pmt))) then
- begin
- DbgLog(self, '*** This filter is accepting an output media type');
- DbgLog(self, ' that it can''t currently transform to. I hope');
- DbgLog(self, ' it''s smart enough to reconnect its input.');
- end;
- {$endif}
- result := FTransformFilter.SetMediaType(PINDIR_OUTPUT,pmt);
- end;
-
- { TCTransInPlaceInputPin }
-
- function TBCTransInPlaceInputPin.CheckMediaType(
- pmt: PAMMediaType): HRESULT;
- begin
- result := FTIPFilter.CheckInputType(pmt);
- if (result <> S_OK) then exit;
- if FTIPFilter.FOutput.IsConnected then
- result := FTIPFilter.FOutput.GetConnected.QueryAccept(pmt^)
- else
- result := S_OK;
- end;
-
- function TBCTransInPlaceInputPin.EnumMediaTypes(
- out ppEnum: IEnumMediaTypes): HRESULT;
- begin
- // Can only pass through if connected
- if (not FTIPFilter.FOutput.IsConnected) then
- begin
- result := VFW_E_NOT_CONNECTED;
- exit;
- end;
-
- result := FTIPFilter.FOutput.GetConnected.EnumMediaTypes(ppEnum);
- end;
-
- function TBCTransInPlaceInputPin.GetAllocator(
- out Allocator: IMemAllocator): HRESULT;
- begin
- FLock.Lock;
- try
- if FTIPFilter.FOutput.IsConnected then
- begin
- // Store the allocator we got
- result := FTIPFilter.OutputPin.ConnectedIMemInputPin.GetAllocator(Allocator);
- if SUCCEEDED(result) then
- FTIPFilter.OutputPin.SetAllocator(Allocator);
- end
- else
- begin
- // Help upstream filter (eg TIP filter which is having to do a copy)
- // by providing a temp allocator here - we'll never use
- // this allocator because when our output is connected we'll
- // reconnect this pin
- result := inherited GetAllocator(Allocator);
- end;
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCTransInPlaceInputPin.GetAllocatorRequirements(
- props: PAllocatorProperties): HRESULT;
- begin
- if FTIPFilter.FOutput.IsConnected then
- result := FTIPFilter.OutputPin.ConnectedIMemInputPin.GetAllocatorRequirements(Props^)
- else
- result := E_NOTIMPL;
- end;
-
- function TBCTransInPlaceInputPin.NotifyAllocator(Allocator: IMemAllocator;
- ReadOnly: BOOL): HRESULT;
- var
- OutputAllocator: IMemAllocator;
- Props, Actual: TAllocatorProperties;
- begin
- result := S_OK;
- FLock.Lock;
- try
- FReadOnly := ReadOnly;
- // If we modify data then don't accept the allocator if it's
- // the same as the output pin's allocator
-
- // If our output is not connected just accept the allocator
- // We're never going to use this allocator because when our
- // output pin is connected we'll reconnect this pin
- if not FTIPFilter.OutputPin.IsConnected then
- begin
- result := inherited NotifyAllocator(Allocator, ReadOnly);
- exit;
- end;
-
- // If the allocator is read-only and we're modifying data
- // and the allocator is the same as the output pin's
- // then reject
- if (FReadOnly and FTIPFilter.FModifiesData) then
- begin
- OutputAllocator := FTIPFilter.OutputPin.PeekAllocator;
-
- // Make sure we have an output allocator
- if (OutputAllocator = nil) then
- begin
- result := FTIPFilter.OutputPin.ConnectedIMemInputPin.GetAllocator(OutputAllocator);
- if FAILED(result) then result := CreateMemoryAllocator(OutputAllocator);
- if SUCCEEDED(result) then
- begin
- FTIPFilter.OutputPin.SetAllocator(OutputAllocator);
- OutputAllocator := nil;
- end;
- end;
- if (Allocator = OutputAllocator) then
- begin
- result := E_FAIL;
- exit;
- end
- else
- if SUCCEEDED(result) then
- begin
- // Must copy so set the allocator properties on the output
- result := Allocator.GetProperties(Props);
- if SUCCEEDED(result) then
- result := OutputAllocator.SetProperties(Props, Actual);
- if SUCCEEDED(result) then
- begin
- if ((Props.cBuffers > Actual.cBuffers)
- or (Props.cbBuffer > Actual.cbBuffer)
- or (Props.cbAlign > Actual.cbAlign)) then
- result := E_FAIL;
-
- end;
-
- // Set the allocator on the output pin
- if SUCCEEDED(result) then
- result := FTIPFilter.OutputPin.ConnectedIMemInputPin.NotifyAllocator(OutputAllocator, FALSE);
- end;
- end
- else
- begin
- result := FTIPFilter.OutputPin.ConnectedIMemInputPin.NotifyAllocator(Allocator, ReadOnly);
- if SUCCEEDED(result) then FTIPFilter.OutputPin.SetAllocator(Allocator);
- end;
-
- if SUCCEEDED(result) then
- begin
- // It's possible that the old and the new are the same thing.
- // AddRef before release ensures that we don't unload it.
- Allocator._AddRef;
- if (FAllocator <> nil) then FAllocator := nil;
- Pointer(FAllocator) := Pointer(Allocator); // We have an allocator for the input pin
- end;
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCTransInPlaceInputPin.PeekAllocator: IMemAllocator;
- begin
- result := FAllocator;
- end;
-
- constructor TBCTransInPlaceInputPin.Create(ObjectName: string;
- Filter: TBCTransInPlaceFilter; out hr: HRESULT; Name: WideString);
- begin
- inherited Create(ObjectName, Filter, hr, Name);
- FReadOnly := FALSE;
- FTIPFilter := Filter;
- DbgLog(self, 'TBCTransInPlaceInputPin.Create');
- end;
-
- { TBCTransInPlaceOutputPin }
-
- function TBCTransInPlaceOutputPin.CheckMediaType(
- pmt: PAMMediaType): HRESULT;
- begin
- // Don't accept any output pin type changes if we're copying
- // between allocators - it's too late to change the input
- // allocator size.
- if (FTIPFilter.UsingDifferentAllocators and (not FFilter.IsStopped)) then
- begin
- if TBCMediaType(pmt).Equal(@Fmt) then result := S_OK else result := VFW_E_TYPE_NOT_ACCEPTED;
- exit;
- end;
-
- // Assumes the type does not change. That's why we're calling
- // CheckINPUTType here on the OUTPUT pin.
- result := FTIPFilter.CheckInputType(pmt);
- if (result <> S_OK) then exit;
- if (FTIPFilter.FInput.IsConnected) then
- result := FTIPFilter.FInput.GetConnected.QueryAccept(pmt^)
- else
- result := S_OK;
- end;
-
- function TBCTransInPlaceOutputPin.ConnectedIMemInputPin: IMemInputPin;
- begin
- pointer(result) := pointer(FInputPin);
- end;
-
- constructor TBCTransInPlaceOutputPin.Create(ObjectName: string;
- Filter: TBCTransInPlaceFilter; out hr: HRESULT; Name: WideString);
- begin
- inherited Create(ObjectName, Filter, hr, Name);
- FTIPFilter := Filter;
- DbgLog(self, 'TBCTransInPlaceOutputPin.Create');
- end;
-
- function TBCTransInPlaceOutputPin.EnumMediaTypes(
- out ppEnum: IEnumMediaTypes): HRESULT;
- begin
- // Can only pass through if connected.
- if not FTIPFilter.FInput.IsConnected then
- result := VFW_E_NOT_CONNECTED
- else
- result := FTIPFilter.FInput.GetConnected.EnumMediaTypes(ppEnum);
- end;
-
- function TBCTransInPlaceOutputPin.PeekAllocator: IMemAllocator;
- begin
- result := FAllocator;
- end;
-
- procedure TBCTransInPlaceOutputPin.SetAllocator(Allocator: IMemAllocator);
- begin
- Allocator._AddRef;
- if(FAllocator <> nil) then FAllocator._Release;
- Pointer(FAllocator) := Pointer(Allocator);
- end;
-
- { TBCTransInPlaceFilter }
-
- function TBCTransInPlaceFilter.CheckTransform(mtIn,
- mtOut: PAMMediaType): HRESULT;
- begin
- result := S_OK;
- end;
-
- // dir is the direction of our pin.
- // pReceivePin is the pin we are connecting to.
-
- function TBCTransInPlaceFilter.CompleteConnect(dir: TPinDirection;
- ReceivePin: IPin): HRESULT;
- var
- pmt: PAMMediaType;
- begin
- ASSERT(FInput <> nil);
- ASSERT(FOutput <> nil);
-
- // if we are not part of a graph, then don't indirect the pointer
- // this probably prevents use of the filter without a filtergraph
- if(FGraph = nil) then
- begin
- result := VFW_E_NOT_IN_GRAPH;
- exit;
- end;
-
- // Always reconnect the input to account for buffering changes
- //
- // Because we don't get to suggest a type on ReceiveConnection
- // we need another way of making sure the right type gets used.
- //
- // One way would be to have our EnumMediaTypes return our output
- // connection type first but more deterministic and simple is to
- // call ReconnectEx passing the type we want to reconnect with
- // via the base class ReconeectPin method.
-
- if(dir = PINDIR_OUTPUT) then
- begin
- if FInput.IsConnected then
- begin
- result := ReconnectPin(FInput, FOutput.AMMediaType);
- exit;
- end;
- result := NOERROR;
- exit;
- end;
-
- ASSERT(dir = PINDIR_INPUT);
-
- // Reconnect output if necessary
-
- if FOutput.IsConnected then
- begin
- pmt := FInput.CurrentMediaType.MediaType;
- if (not TBCMediaType(pmt).Equal(FOutput.CurrentMediaType.MediaType)) then
- begin
- result := ReconnectPin(FOutput, FInput.CurrentMediaType.MediaType);
- exit;
- end;
- end;
- result := NOERROR;
- end;
-
- function TBCTransInPlaceFilter.Copy(Source: IMediaSample): IMediaSample;
- var
- Start, Stop: TReferenceTime;
- Time: boolean;
- pStartTime, pEndTime: PReferenceTime;
- TimeStart, TimeEnd: Int64;
- Flags: DWORD;
- Sample2: IMediaSample2;
- props: PAMSample2Properties;
- MediaType: PAMMediaType;
- DataLength: LongInt;
- SourceBuffer, DestBuffer: PByte;
- SourceSize, DestSize: LongInt;
- hr: hresult;
- begin
- Time := (Source.GetTime(Start, Stop) = S_OK);
- // this may block for an indeterminate amount of time
- if Time then
- begin
- pStartTime := @Start;
- pEndTime := @Stop;
- end
- else
- begin
- pStartTime := nil;
- pEndTime := nil;
- end;
- if FSampleSkipped then Flags := AM_GBF_PREVFRAMESKIPPED else Flags := 0;
- hr := OutputPin.PeekAllocator.GetBuffer(result, pStartTime, pEndTime, Flags);
-
- if FAILED(hr) then exit;
-
- ASSERT(result <> nil);
- if(SUCCEEDED(result.QueryInterface(IID_IMediaSample2, Sample2))) then
- begin
- props := FInput.SampleProps;
- hr := Sample2.SetProperties(SizeOf(TAMSample2Properties) - (4*2), props^);
- Sample2 := nil;
- if FAILED(hr) then
- begin
- result := nil;
- exit;
- end;
- end
- else
- begin
- if Time then result.SetTime(@Start, @Stop);
- if (Source.IsSyncPoint = S_OK) then result.SetSyncPoint(TRUE);
- if ((Source.IsDiscontinuity = S_OK) or FSampleSkipped) then result.SetDiscontinuity(TRUE);
- if (Source.IsPreroll = S_OK) then result.SetPreroll(TRUE);
- // Copy the media type
- if (Source.GetMediaType(MediaType) = S_OK) then
- begin
- result.SetMediaType(MediaType^);
- DeleteMediaType(MediaType);
- end;
-
- end;
-
- FSampleSkipped := FALSE;
-
- // Copy the sample media times
- if (Source.GetMediaTime(TimeStart, TimeEnd) = NOERROR) then
- result.SetMediaTime(@TimeStart,@TimeEnd);
-
- // Copy the actual data length and the actual data.
- DataLength := Source.GetActualDataLength;
-
- result.SetActualDataLength(DataLength);
-
- // Copy the sample data
- SourceSize := Source.GetSize;
- DestSize := result.GetSize;
- {$IFDEF DEBUG}
- DebugLog.SaveToFile('c:\BaseClass.txt');
- {$ENDIF}
- ASSERT(DestSize >= SourceSize, format('DestSize (%d) < SourceSize (%d)',[DestSize, SourceSize]));
- ASSERT(DestSize >= DataLength);
-
- Source.GetPointer(SourceBuffer);
- result.GetPointer(DestBuffer);
- ASSERT((DestSize = 0) or (SourceBuffer <> nil) and (DestBuffer <> nil));
- CopyMemory(DestBuffer, SourceBuffer, DataLength);
- end;
-
- constructor TBCTransInPlaceFilter.Create(ObjectName: string;
- unk: IUnKnown; clsid: TGUID; out hr: HRESULT; ModifiesData: boolean);
- begin
- inherited create(ObjectName, Unk, clsid);
- FModifiesData := ModifiesData;
- end;
-
- constructor TBCTransInPlaceFilter.CreateFromFactory(Factory: TBCClassFactory;
- const Controller: IUnknown);
- begin
- inherited create(FacTory.FName, Controller, FacTory.FClassID);
- FModifiesData := True;
- end;
-
- // Tell the output pin's allocator what size buffers we require.
- // *pAlloc will be the allocator our output pin is using.
-
- function TBCTransInPlaceFilter.DecideBufferSize(Alloc: IMemAllocator;
- propInputRequest: PAllocatorProperties): HRESULT;
- var Request, Actual: TAllocatorProperties;
- begin
- // If we are connected upstream, get his views
- if FInput.IsConnected then
- begin
- // Get the input pin allocator, and get its size and count.
- // we don't care about his alignment and prefix.
- result := InputPin.FAllocator.GetProperties(Request);
- //Request.cbBuffer := 230400;
- if FAILED(result) then exit; // Input connected but with a secretive allocator - enough!
- end
- else
- begin
- // We're reduced to blind guessing. Let's guess one byte and if
- // this isn't enough then when the other pin does get connected
- // we can revise it.
- ZeroMemory(@Request, sizeof(Request));
- Request.cBuffers := 1;
- Request.cbBuffer := 1;
- end;
-
-
- DbgLog(self, 'Setting Allocator Requirements');
- DbgLog(self, format('Count %d, Size %d',[Request.cBuffers, Request.cbBuffer]));
-
- // Pass the allocator requirements to our output side
- // but do a little sanity checking first or we'll just hit
- // asserts in the allocator.
-
- propInputRequest.cBuffers := Request.cBuffers;
- propInputRequest.cbBuffer := Request.cbBuffer;
- if (propInputRequest.cBuffers <= 0) then propInputRequest.cBuffers := 1;
- if (propInputRequest.cbBuffer <= 0) then propInputRequest.cbBuffer := 1;
- result := Alloc.SetProperties(propInputRequest^, Actual);
- if FAILED(result) then exit;
-
- DbgLog(self, 'Obtained Allocator Requirements');
- DbgLog(self, format('Count %d, Size %d, Alignment %d', [Actual.cBuffers, Actual.cbBuffer, Actual.cbAlign]));
-
- // Make sure we got the right alignment and at least the minimum required
-
- if ((Request.cBuffers > Actual.cBuffers)
- or (Request.cbBuffer > Actual.cbBuffer)
- or (Request.cbAlign > Actual.cbAlign)) then
- result := E_FAIL
- else
- result := NOERROR;
- end;
-
- function TBCTransInPlaceFilter.GetMediaType(Position: integer;
- out MediaType: PAMMediaType): HRESULT;
- begin
- DbgLog(self, 'TBCTransInPlaceFilter.GetMediaType should never be called');
- result := E_UNEXPECTED;
- end;
-
- // return a non-addrefed CBasePin * for the user to addref if he holds onto it
- // for longer than his pointer to us. We create the pins dynamically when they
- // are asked for rather than in the constructor. This is because we want to
- // give the derived class an oppportunity to return different pin objects
-
- // As soon as any pin is needed we create both (this is different from the
- // usual transform filter) because enumerators, allocators etc are passed
- // through from one pin to another and it becomes very painful if the other
- // pin isn't there. If we fail to create either pin we ensure we fail both.
-
- function TBCTransInPlaceFilter.GetPin(n: integer): TBCBasePin;
- var hr: HRESULT;
- begin
- hr := S_OK;
- // Create an input pin if not already done
- if(FInput = nil) then
- begin
- FInput := TBCTransInPlaceInputPin.Create('TransInPlace input pin',
- self, // Owner filter
- hr, // Result code
- 'Input'); // Pin name
-
- // Constructor for CTransInPlaceInputPin can't fail
- ASSERT(SUCCEEDED(hr));
- end;
-
- // Create an output pin if not already done
-
- if((FInput <> nil) and (FOutput = nil)) then
- begin
- FOutput := TBCTransInPlaceOutputPin.Create('TransInPlace output pin',
- self, // Owner filter
- hr, // Result code
- 'Output'); // Pin name
-
- // a failed return code should delete the object
- ASSERT(SUCCEEDED(hr));
- if(FOutput = nil) then
- begin
- FInput.Free;
- FInput := nil;
- end;
- end;
-
- // Return the appropriate pin
-
- ASSERT(n in [0,1]);
- case n of
- 0: result := FInput;
- 1: result := FOutput;
- else
- result := nil;
- end;
- end;
-
- function TBCTransInPlaceFilter.InputPin: TBCTransInPlaceInputPin;
- begin
- result := TBCTransInPlaceInputPin(FInput);
- end;
-
- function TBCTransInPlaceFilter.OutputPin: TBCTransInPlaceOutputPin;
- begin
- result := TBCTransInPlaceOutputPin(FOutput);
- end;
-
- function TBCTransInPlaceFilter.Receive(Sample: IMediaSample): HRESULT;
- var Props: PAMSample2Properties;
- begin
- // Check for other streams and pass them on */
- Props := FInput.SampleProps;
- if (Props.dwStreamId <> AM_STREAM_MEDIA) then
- begin
- result := FOutput.Deliver(Sample);
- exit;
- end;
-
- if UsingDifferentAllocators then
- begin
- // We have to copy the data.
- Sample := Copy(Sample);
- if (Sample = nil) then
- begin
- result := E_UNEXPECTED;
- exit;
- end;
- end;
-
- // have the derived class transform the data
- result := Transform(Sample);
-
- if FAILED(result) then
- begin
- DbgLog(self, 'Error from TransInPlace');
- if UsingDifferentAllocators then Sample := nil;
- exit;
- end;
-
- // the Transform() function can return S_FALSE to indicate that the
- // sample should not be delivered; we only deliver the sample if it's
- // really S_OK (same as NOERROR, of course.)
- if (result = NOERROR) then
- result := FOutput.Deliver(Sample)
- else
- begin
- // But it would be an error to return this private workaround
- // to the caller ...
- if (result = S_FALSE) then
- begin
- // S_FALSE returned from Transform is a PRIVATE agreement
- // We should return NOERROR from Receive() in this cause because
- // returning S_FALSE from Receive() means that this is the end
- // of the stream and no more data should be sent.
- FSampleSkipped := TRUE;
- if (not FQualityChanged) then
- begin
- NotifyEvent(EC_QUALITY_CHANGE,0,0);
- FQualityChanged := TRUE;
- end;
- result := NOERROR;
- end;
- end;
-
- // release the output buffer. If the connected pin still needs it,
- // it will have addrefed it itself.
- if UsingDifferentAllocators then Sample := nil;
- end;
-
- function TBCTransInPlaceFilter.TypesMatch: boolean;
- var
- pmt: PAMMediaType;
- begin
- pmt := InputPin.CurrentMediaType.MediaType;
- result := TBCMediaType(pmt).Equal(OutputPin.CurrentMediaType.MediaType);
- end;
-
- function TBCTransInPlaceFilter.UsingDifferentAllocators: boolean;
- begin
- result := Pointer(InputPin.FAllocator) <> Pointer(OutputPin.FAllocator);
- end;
-
- { TBCBasePropertyPage }
-
- function TBCBasePropertyPage.Activate(hwndParent: HWnd; const rc: TRect;
- bModal: BOOL): HResult;
- begin
- // Return failure if SetObject has not been called.
- if (FObjectSet = FALSE) or (hwndParent = 0) then
- begin
- result := E_UNEXPECTED;
- exit;
- end;
-
- // FForm := TCustomFormClass(FFormClass).Create(nil);
-
- if (FForm = nil) then
- begin
- result := E_OUTOFMEMORY;
- exit;
- end;
-
- FForm.ParentWindow := hwndParent;
- if assigned(FForm.OnActivate) then FForm.OnActivate(FForm);
- Move(rc);
- result := Show(SW_SHOWNORMAL);
- end;
-
- function TBCBasePropertyPage.Apply: HResult;
- begin
- // In ActiveMovie 1.0 we used to check whether we had been activated or
- // not. This is too constrictive. Apply should be allowed as long as
- // SetObject was called to set an object. So we will no longer check to
- // see if we have been activated (ie., m_hWnd != NULL), but instead
- // make sure that m_bObjectSet is TRUE (ie., SetObject has been called).
-
- if (FObjectSet = FALSE) or (FPageSite = nil) then
- begin
- result := E_UNEXPECTED;
- exit;
- end;
-
- if (FDirty = FALSE) then
- begin
- result := NOERROR;
- exit;
- end;
-
- // Commit derived class changes
-
- result := FForm.OnApplyChanges;
- if SUCCEEDED(result) then FDirty := FALSE;
- end;
-
- function TBCBasePropertyPage.Deactivate: HResult;
- var Style: DWORD;
- begin
- if (FForm = nil) then
- begin
- result := E_UNEXPECTED;
- exit;
- end;
-
- // Remove WS_EX_CONTROLPARENT before DestroyWindow call
-
- Style := GetWindowLong(FForm.Handle, GWL_EXSTYLE);
- Style := Style and (not WS_EX_CONTROLPARENT);
-
- // Set m_hwnd to be NULL temporarily so the message handler
- // for WM_STYLECHANGING doesn't add the WS_EX_CONTROLPARENT
- // style back in
-
- SetWindowLong(FForm.Handle, GWL_EXSTYLE, Style);
- if assigned(FForm.OnDeactivate) then FForm.OnDeactivate(FForm);
-
- // Destroy the dialog window
-
- //FForm.Free;
- //FForm := nil;
- result := NOERROR;
- end;
-
- function TBCBasePropertyPage.GetPageInfo(out pageInfo: TPropPageInfo): HResult;
- begin
- pageInfo.cb := sizeof(TPropPageInfo);
- AMGetWideString(FForm.Caption, pageInfo.pszTitle);
- PageInfo.pszDocString := nil;
- PageInfo.pszHelpFile := nil;
- PageInfo.dwHelpContext:= 0;
- PageInfo.size.cx := FForm.width;
- PageInfo.size.cy := FForm.Height;
- Result := NoError;
- end;
-
- function TBCBasePropertyPage.Help(pszHelpDir: POleStr): HResult;
- begin
- result := E_NOTIMPL;
- end;
-
- function TBCBasePropertyPage.IsPageDirty: HResult;
- begin
- if FDirty then result := S_OK else result := S_FALSE;
- end;
-
- function TBCBasePropertyPage.Move(const rect: TRect): HResult;
- begin
- if (FForm = nil) then
- begin
- result := E_UNEXPECTED;
- exit;
- end;
-
- MoveWindow(FForm.Handle, // Property page handle
- Rect.left, // x coordinate
- Rect.top, // y coordinate
- Rect.Right - Rect.Left, // Overall window width
- Rect.Bottom - Rect.Top, // And likewise height
- TRUE); // Should we repaint it
-
- result := NOERROR;
- end;
-
- function TBCBasePropertyPage.SetObjects(cObjects: Integer;
- pUnkList: PUnknownList): HResult;
- begin
- if (cObjects = 1) then
- begin
- if (pUnkList = nil) then
- begin
- result := E_POINTER;
- exit;
- end;
- // Set a flag to say that we have set the Object
- FObjectSet := TRUE ;
- result := FForm.OnConnect(pUnkList^[0]);
- exit;
- end
- else
- if (cObjects = 0) then
- begin
- // Set a flag to say that we have not set the Object for the page
- FObjectSet := FALSE;
- result := FForm.OnDisconnect;
- exit;
- end;
-
- DbgLog(self, 'No support for more than one object');
- result := E_UNEXPECTED;
- end;
-
- function TBCBasePropertyPage.SetPageSite(
- const pageSite: IPropertyPageSite): HResult;
- begin
- if (pageSite <> nil) then
- begin
- if (FPageSite <> nil) then
- begin
- result := E_UNEXPECTED;
- exit;
- end;
- FPageSite := pageSite;
- end
- else
- begin
- if (FPageSite = nil) then
- begin
- result := E_UNEXPECTED;
- exit;
- end;
- FPageSite := nil;
- end;
- result := NOERROR;
- end;
-
- function TBCBasePropertyPage.Show(nCmdShow: Integer): HResult;
- begin
- if (FForm = nil) then
- begin
- result := E_UNEXPECTED;
- exit;
- end;
-
- if ((nCmdShow <> SW_SHOW) and (nCmdShow <> SW_SHOWNORMAL) and (nCmdShow <> SW_HIDE)) then
- begin
- result := E_INVALIDARG;
- exit;
- end;
-
- if nCmdShow in [SW_SHOW,SW_SHOWNORMAL] then FForm.Show else FForm.Hide;
- InvalidateRect(FForm.Handle, nil, TRUE);
- result := NOERROR;
- end;
-
- function TBCBasePropertyPage.TranslateAccelerator(msg: PMsg): HResult;
- begin
- result := E_NOTIMPL;
- end;
-
- constructor TBCBasePropertyPage.Create(Name: String; Unk: IUnKnown; Form: TFormPropertyPage);
- begin
- inherited Create(Name, Unk);
- FForm := Form;
- FForm.BorderStyle := bsNone;
- FPageSite := nil;
- FObjectSet := false;
- FDirty := false;
- end;
-
- destructor TBCBasePropertyPage.Destroy;
- begin
- if FForm <> nil then
- begin
- FForm.Free;
- FForm := nil;
- end;
- inherited;
- end;
-
- function TFormPropertyPage.OnApplyChanges: HRESULT;
- begin
- result := NOERROR;
- end;
-
- function TFormPropertyPage.OnConnect(Unknown: IUnKnown): HRESULT;
- begin
- result := NOERROR;
- end;
-
- function TFormPropertyPage.OnDisconnect: HRESULT;
- begin
- result := NOERROR;
- end;
-
- procedure TBCBasePropertyPage.SetPageDirty;
- begin
- FDirty := True;
- end;
-
- { TBCBaseDispatch }
-
- function TBCBaseDispatch.GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
- var ti: ITypeInfo;
- begin
- // although the IDispatch riid is dead, we use this to pass from
- // the interface implementation class to us the iid we are talking about.
- result := GetTypeInfo(iid, 0, LocaleID, ti);
- if SUCCEEDED(result) then
- result := ti.GetIDsOfNames(Names, NameCount, DispIDs);
- end;
-
- function TBCBaseDispatch.GetTypeInfo(const iid: TGUID; info: Cardinal; lcid: LCID;
- out tinfo): HRESULT; stdcall;
- var
- tlib : ITypeLib;
- begin
- // we only support one type element
- if (info <> 0) then
- begin
- result := TYPE_E_ELEMENTNOTFOUND;
- exit;
- end;
-
- // always look for neutral
- if (FTI = nil) then
- begin
- result := LoadRegTypeLib(LIBID_QuartzTypeLib, 1, 0, lcid, tlib);
- if FAILED(result) then
- begin
- result := LoadTypeLib('control.tlb', tlib);
- if FAILED(result) then exit;
- end;
- result := tlib.GetTypeInfoOfGuid(iid, Fti);
- tlib := nil;
- if FAILED(result) then exit;
- end;
- ITypeInfo(tinfo) := Fti;
- result := S_OK;
- end;
-
- function TBCBaseDispatch.GetTypeInfoCount(out Count: Integer): HResult;
- begin
- count := 1;
- result := S_OK;
- end;
-
- { TBCMediaControl }
-
- constructor TBCMediaControl.Create(name: string; unk: IUnknown);
- begin
- FBaseDisp := TBCBaseDispatch.Create;
- end;
-
- destructor TBCMediaControl.Destroy;
- begin
- FBaseDisp.Free;
- inherited;
- end;
-
- function TBCMediaControl.GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
- begin
- result := FBasedisp.GetIDsOfNames(IID_IMediaControl, Names, NameCount, LocaleID, DispIDs);
- end;
-
- function TBCMediaControl.GetTypeInfo(Index, LocaleID: Integer;
- out TypeInfo): HResult;
- begin
- result := Fbasedisp.GetTypeInfo(IID_IMediaControl, index, LocaleID, TypeInfo);
- end;
-
- function TBCMediaControl.GetTypeInfoCount(out Count: Integer): HResult;
- begin
- result := FBaseDisp.GetTypeInfoCount(Count);
- end;
-
- function TBCMediaControl.Invoke(DispID: Integer; const IID: TGUID;
- LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
- ArgErr: Pointer): HResult;
- var ti: ITypeInfo;
- begin
- // this parameter is a dead leftover from an earlier interface
- if not IsEqualGUID(GUID_NULL, IID) then
- begin
- result := DISP_E_UNKNOWNINTERFACE;
- exit;
- end;
- result := GetTypeInfo(0, LocaleID, ti);
- if FAILED(result) then exit;
- result := ti.Invoke(Pointer(Integer(Self)), DISPID, Flags, TDispParams(Params),
- VarResult, ExcepInfo, ArgErr);
- end;
-
- { TBCMediaEvent }
-
- constructor TBCMediaEvent.Create(Name: string; Unk: IUnknown);
- begin
- inherited Create(name, Unk);
- FBasedisp := TBCBaseDispatch.Create;
- end;
-
- destructor TBCMediaEvent.destroy;
- begin
- FBasedisp.Free;
- inherited;
- end;
-
- function TBCMediaEvent.GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
- begin
- result := FBasedisp.GetIDsOfNames(IID_IMediaEvent, Names, NameCount, LocaleID, DispIDs);
- end;
-
- function TBCMediaEvent.GetTypeInfo(Index, LocaleID: Integer;
- out TypeInfo): HResult;
- begin
- result := Fbasedisp.GetTypeInfo(IID_IMediaEvent, index, LocaleID, TypeInfo);
- end;
-
- function TBCMediaEvent.GetTypeInfoCount(out Count: Integer): HResult;
- begin
- result := FBaseDisp.GetTypeInfoCount(Count);
- end;
-
- function TBCMediaEvent.Invoke(DispID: Integer; const IID: TGUID;
- LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
- ArgErr: Pointer): HResult;
- var ti: ITypeInfo;
- begin
- // this parameter is a dead leftover from an earlier interface
- if not IsEqualGUID(GUID_NULL, IID) then
- begin
- result := DISP_E_UNKNOWNINTERFACE;
- exit;
- end;
- result := GetTypeInfo(0, LocaleID, ti);
- if FAILED(result) then exit;
- result := ti.Invoke(Pointer(Integer(Self)), DISPID, Flags, TDispParams(Params), VarResult, ExcepInfo, ArgErr);
- end;
-
- { TBCMediaPosition }
-
- constructor TBCMediaPosition.Create(Name: String; Unk: IUnknown);
- begin
- inherited Create(Name, Unk);
- FBaseDisp := TBCBaseDispatch.Create;
- end;
-
- constructor TBCMediaPosition.Create(Name: String; Unk: IUnknown;
- out hr: HRESULT);
- begin
- inherited Create(Name, Unk);
- FBaseDisp := TBCBaseDispatch.Create;
- end;
-
- destructor TBCMediaPosition.Destroy;
- begin
- FBaseDisp.Free;
- inherited;
- end;
-
- function TBCMediaPosition.GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
- begin
- result := FBasedisp.GetIDsOfNames(IID_IMediaPosition, Names, NameCount, LocaleID, DispIDs);
- end;
-
- function TBCMediaPosition.GetTypeInfo(Index, LocaleID: Integer;
- out TypeInfo): HResult;
- begin
- result := Fbasedisp.GetTypeInfo(IID_IMediaPosition, index, LocaleID, TypeInfo);
- end;
-
- function TBCMediaPosition.GetTypeInfoCount(out Count: Integer): HResult;
- begin
- result := Fbasedisp.GetTypeInfoCount(Count);
- end;
-
- function TBCMediaPosition.Invoke(DispID: Integer; const IID: TGUID;
- LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
- ArgErr: Pointer): HResult;
- var ti: ITypeInfo;
- begin
- // this parameter is a dead leftover from an earlier interface
- if not IsEqualGUID(GUID_NULL, IID) then
- begin
- result := DISP_E_UNKNOWNINTERFACE;
- exit;
- end;
- result := GetTypeInfo(0, LocaleID, ti);
- if FAILED(result) then exit;
- result := ti.Invoke(Pointer(Integer(Self)), DISPID, Flags, TDispParams(Params), VarResult, ExcepInfo, ArgErr);
- end;
-
- { TBCPosPassThru }
-
- function TBCPosPassThru.CanSeekBackward(
- out pCanSeekBackward: Integer): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.CanSeekBackward(pCanSeekBackward);
- end;
-
- function TBCPosPassThru.CanSeekForward(
- out pCanSeekForward: Integer): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.CanSeekForward(pCanSeekForward);
- end;
-
- function TBCPosPassThru.CheckCapabilities(
- var pCapabilities: DWORD): HRESULT;
- var
- MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.CheckCapabilities(pCapabilities);
- end;
-
- function TBCPosPassThru.ConvertTimeFormat(out pTarget: int64;
- pTargetFormat: PGUID; Source: int64; pSourceFormat: PGUID): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat);
- end;
-
- constructor TBCPosPassThru.Create(name: String; Unk: IUnknown;
- out hr: HRESULT; Pin: IPin);
- begin
- assert(Pin <> nil);
- inherited Create(Name,Unk);
- FPin := Pin;
- end;
-
- function TBCPosPassThru.ForceRefresh: HRESULT;
- begin
- result := S_OK;
- end;
-
- function TBCPosPassThru.get_CurrentPosition(
- out pllTime: TRefTime): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.get_CurrentPosition(pllTime);
- end;
-
- function TBCPosPassThru.get_Duration(out plength: TRefTime): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.get_Duration(plength);
- end;
-
- function TBCPosPassThru.get_PrerollTime(out pllTime: TRefTime): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.get_PrerollTime(pllTime);
- end;
-
- function TBCPosPassThru.get_Rate(out pdRate: double): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.get_Rate(pdRate);
- end;
-
- function TBCPosPassThru.get_StopTime(out pllTime: TRefTime): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.get_StopTime(pllTime);
- end;
-
- function TBCPosPassThru.GetAvailable(out pEarliest,
- pLatest: int64): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.GetAvailable(pEarliest, pLatest);
- end;
-
- function TBCPosPassThru.GetCapabilities(out pCapabilities: DWORD): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.GetCapabilities(pCapabilities);
- end;
-
- function TBCPosPassThru.GetCurrentPosition(out pCurrent: int64): HRESULT;
- var
- MS: IMediaSeeking;
- Stop: int64;
- begin
- result := GetMediaTime(pCurrent, Stop);
- if SUCCEEDED(result) then
- result := NOERROR
- else
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.GetCurrentPosition(pCurrent)
- end;
- end;
-
- function TBCPosPassThru.GetDuration(out pDuration: int64): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.GetDuration(pDuration);
- end;
-
- function TBCPosPassThru.GetMediaTime(out StartTime,
- EndTime: Int64): HRESULT;
- begin
- result := E_FAIL;
- end;
-
- // Return the IMediaPosition interface from our peer
-
- function TBCPosPassThru.GetPeer(out MP: IMediaPosition): HRESULT;
- var
- Connected: IPin;
- begin
- result := FPin.ConnectedTo(Connected);
- if FAILED(result) then
- begin
- result := E_NOTIMPL;
- exit;
- end;
-
- result := Connected.QueryInterface(IID_IMediaPosition, MP);
- Connected := nil;
- if FAILED(result) then
- begin
- result := E_NOTIMPL;
- exit;
- end;
- result := S_OK;
- end;
-
- function TBCPosPassThru.GetPeerSeeking(out MS: IMediaSeeking): HRESULT;
- var
- Connected: IPin;
- begin
- MS := nil;
-
- result := FPin.ConnectedTo(Connected);
- if FAILED(result) then
- begin
- result := E_NOTIMPL;
- exit;
- end;
-
- result := Connected.QueryInterface(IID_IMediaSeeking, MS);
- Connected := nil;
- if FAILED(result) then
- begin
- result := E_NOTIMPL;
- exit;
- end;
-
- result := S_OK;
- end;
-
- function TBCPosPassThru.GetPositions(out pCurrent, pStop: int64): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.GetPositions(pCurrent, pStop);
- end;
-
- function TBCPosPassThru.GetPreroll(out pllPreroll: int64): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.GetPreroll(pllPreroll);
- end;
-
- function TBCPosPassThru.GetRate(out pdRate: double): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.GetRate(pdRate);
- end;
-
- function TBCPosPassThru.GetStopPosition(out pStop: int64): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.GetStopPosition(pStop);
- end;
-
- function TBCPosPassThru.GetTimeFormat(out pFormat: TGUID): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.GetTimeFormat(pFormat);
- end;
-
- function TBCPosPassThru.IsFormatSupported(const pFormat: TGUID): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.IsFormatSupported(pFormat);
- end;
-
- function TBCPosPassThru.IsUsingTimeFormat(const pFormat: TGUID): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.IsUsingTimeFormat(pFormat);
- end;
-
- function TBCPosPassThru.put_CurrentPosition(llTime: TRefTime): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.put_CurrentPosition(llTime);
- end;
-
- function TBCPosPassThru.put_PrerollTime(llTime: TRefTime): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.put_PrerollTime(llTime);
- end;
-
- function TBCPosPassThru.put_Rate(dRate: double): HResult;
- var MP: IMediaPosition;
- begin
- if (dRate = 0.0) then
- begin
- result := E_INVALIDARG;
- exit;
- end;
-
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.put_Rate(dRate);
- end;
-
- function TBCPosPassThru.put_StopTime(llTime: TRefTime): HResult;
- var MP: IMediaPosition;
- begin
- result := GetPeer(MP);
- if FAILED(result) then exit;
- result := MP.put_StopTime(llTime);
- end;
-
- function TBCPosPassThru.QueryPreferredFormat(out pFormat: TGUID): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.QueryPreferredFormat(pFormat);
- end;
-
- function TBCPosPassThru.SetPositions(var pCurrent: int64;
- dwCurrentFlags: DWORD; var pStop: int64; dwStopFlags: DWORD): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.SetPositions(pCurrent, dwCurrentFlags, pStop, dwStopFlags);
- end;
-
- function TBCPosPassThru.SetRate(dRate: double): HRESULT;
- var MS: IMediaSeeking;
- begin
- if (dRate = 0.0) then
- begin
- result := E_INVALIDARG;
- exit;
- end;
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.SetRate(dRate);
- end;
-
- function TBCPosPassThru.SetTimeFormat(const pFormat: TGUID): HRESULT;
- var MS: IMediaSeeking;
- begin
- result := GetPeerSeeking(MS);
- if FAILED(result) then exit;
- result := MS.SetTimeFormat(pFormat);
- end;
-
- { TBCRendererPosPassThru }
-
- // Media times (eg current frame, field, sample etc) are passed through the
- // filtergraph in media samples. When a renderer gets a sample with media
- // times in it, it will call one of the RegisterMediaTime methods we expose
- // (one takes an IMediaSample, the other takes the media times direct). We
- // store the media times internally and return them in GetCurrentPosition.
-
- constructor TBCRendererPosPassThru.Create(name: String; Unk: IUnknown;
- out hr: HRESULT; Pin: IPin);
- begin
- inherited Create(Name,Unk,hr,Pin);
- FStartMedia:= 0;
- FEndMedia := 0;
- FReset := TRUE;
- FPositionLock := TBCCritSec.Create;
- end;
-
- destructor TBCRendererPosPassThru.destroy;
- begin
- FPositionLock.Free;
- inherited;
- end;
-
- // Intended to be called by the owing filter during EOS processing so
- // that the media times can be adjusted to the stop time. This ensures
- // that the GetCurrentPosition will actully get to the stop position.
-
- function TBCRendererPosPassThru.EOS: HRESULT;
- var Stop: int64;
- begin
- if FReset then result := E_FAIL
- else
- begin
- result := GetStopPosition(Stop);
- if SUCCEEDED(result) then
- begin
- FPositionLock.Lock;
- try
- FStartMedia := Stop;
- FEndMedia := Stop;
- finally
- FPositionLock.UnLock;
- end;
- end;
- end;
- end;
-
- function TBCRendererPosPassThru.GetMediaTime(out StartTime,
- EndTime: int64): HRESULT;
- begin
- FPositionLock.Lock;
- try
- if FReset then
- begin
- result := E_FAIL;
- exit;
- end;
- // We don't have to return the end time
- result := ConvertTimeFormat(StartTime, nil, FStartMedia, @TIME_FORMAT_MEDIA_TIME);
- if SUCCEEDED(result) then
- result := ConvertTimeFormat(EndTime, nil, FEndMedia, @TIME_FORMAT_MEDIA_TIME);
- finally
- FPositionLock.UnLock;
- end;
- end;
-
- // Sets the media times the object should report
-
- function TBCRendererPosPassThru.RegisterMediaTime(
- MediaSample: IMediaSample): HRESULT;
- var StartMedia, EndMedia: TReferenceTime;
- begin
- ASSERT(assigned(MediaSample));
- FPositionLock.Lock;
- try
- // Get the media times from the sample
- result := MediaSample.GetTime(StartMedia, EndMedia);
- if FAILED(result) then
- begin
- ASSERT(result = VFW_E_SAMPLE_TIME_NOT_SET);
- exit;
- end;
- FStartMedia := StartMedia;
- FEndMedia := EndMedia;
- FReset := FALSE;
- result := NOERROR;
- finally
- FPositionLock.Unlock;
- end;
- end;
-
- // Sets the media times the object should report
-
- function TBCRendererPosPassThru.RegisterMediaTime(StartTime,
- EndTime: int64): HRESULT;
- begin
- FPositionLock.Lock;
- try
- FStartMedia := StartTime;
- FEndMedia := EndTime;
- FReset := FALSE;
- result := NOERROR;
- finally
- FPositionLock.UnLock;
- end;
- end;
-
- // Resets the media times we hold
-
- function TBCRendererPosPassThru.ResetMediaTime: HRESULT;
- begin
- FPositionLock.Lock;
- try
- FStartMedia := 0;
- FEndMedia := 0;
- FReset := TRUE;
- result := NOERROR;
- finally
- FPositionLock.UnLock;
- end;
- end;
-
- { TBCAMEvent }
-
- function TBCAMEvent.Check: boolean;
- begin
- result := Wait(0);
- end;
-
- constructor TBCAMEvent.Create(ManualReset: boolean);
- begin
- FEvent := CreateEvent(nil, ManualReset, FALSE, nil);
- end;
-
- destructor TBCAMEvent.destroy;
- begin
- if FEvent <> 0 then CloseHandle(FEvent);
- inherited;
- end;
-
- procedure TBCAMEvent.Reset;
- begin
- ResetEvent(FEvent);
- end;
-
- procedure TBCAMEvent.SetEv;
- begin
- SetEvent(FEvent);
- end;
-
- function TBCAMEvent.Wait(Timeout: Cardinal): boolean;
- begin
- result := (WaitForSingleObject(FEvent, Timeout) = WAIT_OBJECT_0);
- end;
-
- { TBCRenderedInputPin }
-
- function TBCRenderedInputPin.Active: HRESULT;
- begin
- FAtEndOfStream := FALSE;
- FCompleteNotified := FALSE;
- result := inherited Active;
- end;
-
- constructor TBCRenderedInputPin.Create(ObjectName: string;
- Filter: TBCBaseFilter; Lock: TBCCritSec; out hr: HRESULT;
- Name: WideString);
- begin
- inherited Create(ObjectName, Filter, Lock, hr, Name);
- FAtEndOfStream := FALSE;
- FCompleteNotified := FALSE;
- end;
-
- procedure TBCRenderedInputPin.DoCompleteHandling;
- begin
- ASSERT(FAtEndOfStream);
- if (not FCompleteNotified) then
- begin
- FCompleteNotified := TRUE;
- FFilter.NotifyEvent(EC_COMPLETE, S_OK, Integer(FFilter));
- end;
- end;
-
- function TBCRenderedInputPin.EndFlush: HRESULT;
- begin
- FLock.Lock;
- try
- // Clean up renderer state
- FAtEndOfStream := FALSE;
- FCompleteNotified := FALSE;
- result := inherited EndFlush;
- finally
- FLock.UnLock;
- end;
- end;
-
- function TBCRenderedInputPin.EndOfStream: HRESULT;
- var
- fs: TFilterState;
- begin
- result := CheckStreaming;
- // Do EC_COMPLETE handling for rendered pins
- if ((result = S_OK) and (not FAtEndOfStream)) then
- begin
- FAtEndOfStream := TRUE;
- ASSERT(SUCCEEDED(FFilter.GetState(0, fs)));
- if (fs = State_Running) then
- DoCompleteHandling;
- end;
- end;
-
- function TBCRenderedInputPin.Run(Start: TReferenceTime): HRESULT;
- begin
- FCompleteNotified := FALSE;
- if FAtEndOfStream then DoCompleteHandling;
- result := S_OK;
- end;
-
- initialization
- {$IFDEF DEBUG}
- DebugLog := TStringList.Create;
- {$ENDIF}
-
- finalization
- begin
- if TemplatesVar <> nil then TemplatesVar.Free;
- TemplatesVar := nil;
- {$IFDEF DEBUG}
- DebugLog.Add(format('FactoryCount: %d, ObjectCount: %d.',[FactoryCount, ObjectCount]));
- DebugLog.SaveToFile('c:\BaseClass.txt');
- DebugLog.Free;
- {$ENDIF}
- end;
-
- end.
-
-